nyoj 133 子序列 (离散化+尺取法)

来源:互联网 发布:java ftp上传图片损坏 编辑:程序博客网 时间:2024/04/29 09:26

子序列

时间限制:3000 ms  |  内存限制:65535 KB
难度:5
描述

给定一个序列,请你求出该序列的一个连续的子序列,使原串中出现的所有元素皆在该子序列中出现过至少1次。

如2 8 8 8 1 1,所求子串就是2 8 8 8 1。

输入
第一行输入一个整数T(0<T<=5)表示测试数据的组数
每组测试数据的第一行是一个整数N(1<=N<=1000000),表示给定序列的长度。
随后的一行有N个正整数,表示给定的序列中的所有元素。
数据保证输入的整数都不会超出32位整数的范围。
 
输出
对于每组输入,输出包含该序列中所有元素的最短子序列的长度
样例输入
251 8 8 8 162 8 8 8 1 1
样例输出
25
  **求所有元素出现一次的最短子序列
思路  离散化去重复,尺取法找最短子序列
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int maxn=1000010;int T;int num[maxn];int a[maxn];int main(){    scanf("%d",&T);    while(T--)    {        int n;        scanf("%d",&n);        for(int i=1; i<=n; i++)        {            scanf("%d",&num[i]);            a[i]=num[i];        }//离散化         sort(a+1,a+n+1);        int cnt=1;        for(int i=2;i<=n;i++)//去重复,cnt表示不相同数据的总数         {            if(a[i]!=a[i-1])                a[++cnt]=a[i];        }        int maxx=cnt;              for(int i=1;i<=n;i++) //将num[]中的数据对应为 a[] 中数据的下标         {            int l=1,r=cnt;            int mid;            while(l<=r)//二分查找位置             {                mid=(l+r)>>1;                if(a[mid]==num[i])                {                    num[i]=mid;                                   break;                }                else if(a[mid]<num[i])                    l=mid+1;                else                    r=mid-1;            }        }//尺取法         int l=0,sum=0,ans=maxn;        int vis[maxn];                memset(vis,0,sizeof(vis));        for(int i=1;i<=n;i++)                {            if(vis[num[i]]==0) sum++;            vis[num[i]]++;            while(sum==maxx)//当取完所有种类的数后             {                vis[num[l]]--;//移动左界                 if(vis[num[l]]==0) sum--;                ans=min(i-l+1,ans);//跟新答案                 l++;//移动左界             }        }        printf("%d\n",ans);    }    return 0;}

0 0
原创粉丝点击