UVa 11572

本文介绍了两种高效求解最长无重复子序列的方法,并通过一道关于滑动窗口内的最小值问题进一步探讨了如何利用合适的数据结构来简化代码并提高算法效率。

题目:输入一个长度为n的序列A,找到一个尽量长的连续子序列,使得该序列中没有相同的元素。

代码:

方法一

#include<iostream>
#include<set>
#include<algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;

const int Max=1000000+5;
int A[Max];

int main(){
    int T;
    while(cin>>T){
         while(T--){
              int n;
              cin>>n;
              for(int i=0;i<n;i++){
                      cin>>A[i];
              }
              
              set<int> s;
              int L=0,R=0,ans=0;
              while(R<n){
                   while(R<n&&!s.count(A[R])){
                        s.insert(A[R++]);
                   }
                   
                   ans=max(ans,R-L);
         
                   s.erase(A[L++]);
              }
              cout<<ans<<endl;
         }
    }
    return 0;
}

方法二

#include<iostream>
#include<map>
#define max(a,b) ((a>b)?(a):(b))
using namespace std;

const int Max=1000000+5;
int a[Max],last[Max];
map<int,int> cur;

int main(){
    int T;
    while(cin>>T){
        while(T--){
             int n;
             cin>>n;
             for(int i=0;i<n;i++){
                     cin>>a[i];
                     if(!cur.count(a[i])) last[i]=-1;
                     else last[i]=cur[a[i]];
                     cur[a[i]]=i;
             }
             
             int L=0,R=0,ans=0;
             while(R<n){
                  while(R<n&&last[R]<L){
                       R++;
                  }
                  ans=max(ans,R-L);
                
                  L++;
             }
             
             cout<<ans<<endl;
             cur.clear();
        }
    }
    return 0;
}

 

分析:以上是书(算法竞赛经典入门)中的两中解决方法,方法一用的是集合容器来解决重复问题,方法二使用的是存储下标来处理重叠。可见使用适合的数据结构不仅能简化代码,还能高效地解决问题。

借助这些作为启发,我对后面的一道题进行了解答。

题目是:输入一个正整数k和一个长度为n的整数序列,定义f(i)表示从元素i开始的连续k个元素的最小值,从f(i开始一直到f(n-k+1)输出。

这个题在书上作为思考题是没有答案的,以下是我的代码:

#include<iostream>
#include<cstdio>
#include<memory.h>
using namespace std;
const int Max=1000+5;

int main(){
    int T;
    while(cin>>T){
         while(T--){
              int a[Max],b[Max];
              int n,k;
              cin>>n>>k;
              for(int i=0;i<n;i++){
                      cin>>a[i];
              }
              memset(b,0,sizeof(b));
              int front,rear,num=0;
              for(int j=0;j<k;j++){
                 if(b[0]==0) b[0]=a[j];
                 else{
                 if(a[j]<b[0]){
                 b[0]=a[j];
                 for(int j=1;j<num;j++) b[j]=0;
                 num=0;
                 }else if(a[j]>b[num]&&b[num]!=0) b[++num]=a[j];
                 }
              }
              cout<<b[0]<<endl;
              front=0;rear=k-1;
              while(rear<n-1){
                   if(a[front]==b[0]){
                      for(int i=0;i<num;i++)
                      b[i]=b[i+1];
                      num--;
                   }
                   front++;
                   if(a[rear+1]>b[num]){
                       b[++num]=a[rear+1];
                   }else{
                       if(a[rear+1]<b[0])
                       {
                          for(int i=0;i<=num;i++) b[i]=0;
                          b[0]=a[rear+1];
                          num=0;
                       }
                   }
                   rear++;
                   cout<<b[0]<<endl;
                   }
              }
         }
    return 0;
}

这道题采用的是滑动窗口的形式来改变范围,并使其进入准备好的单调序列。正是因为不知道最小值什么时候被丢出序列,所以要保存好上一状态的单调序列。其时间复杂度比理想的O(n)大(此处没有使用书上建议的单调队列,考虑到内存问题,用时间去换内存),但是比每次检索最小值的复杂度O((n-k)log(k))一般要小。

 

如有疑问或对代码的改进有建议,请给我留言,谢谢。



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值