11536 - Smallest Sub-Array(滑动窗口)

该题其实就是lrj讲的滑动窗口的一个小小变形, 只不过这个窗口的大小是可变的 。  具体方法其实和例题Shuffle类似,用首尾两个指针维护窗口,用一个数组cnt记录1~k中每个数字在窗口中出现的次数,用一个变量c记录窗口中只出现了一次的数字的个数 。   这样只要c == k 这就是一个满足条件的答案,取最小答案即可 。

由于每个元素都只插入删除一次,所以时间复杂度为O(n)。

该算法还有一个名字叫“取尺法” ,特点是处理一段长度未知的连续区间,通过反复地推进区间的开头和末尾 ,直到得出答案的方法 。

细节参见代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000 + 10;
int T,n,m,k,Case = 0,a[maxn],cnt[maxn];
map<int,int> p;
vector<int> g[105];
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d%d",&n,&m,&k);
        memset(cnt,0,sizeof(cnt));
        a[1] = 1; a[2] = 2; a[3] = 3;
        if(n>3) for(int i=4;i<=n;i++) {
            a[i] = (a[i-1]+a[i-2]+a[i-3])%m + 1;
        }
        int ans = 2000000000;
        int rear = 0,last = 1,c = 0;
        while(true) {
            if(c == k) {
                cnt[a[last]] -- ;
                if(cnt[a[last]] == 0 && a[last] <= k) c--;
                last++;
                if(c == k) ans = min(ans , rear - last + 1);
            }
            else {
                rear ++;
                if(rear > n) break;
                cnt[a[rear]]++;
                if(cnt[a[rear]] == 1 && a[rear] <= k) c++;

                if(c == k) ans = min(ans , rear - last + 1);

            }
        }
        printf("Case %d: ",++Case);
        if(ans != 2000000000) printf("%d\n",ans);
        else printf("sequence nai\n");
    }
    return 0;
}


你可能感兴趣的:(ACM,uva)