Jessica's Reading Problem
题意:
一本书有若干个知识点,不同的数字代表不同的知识点,要求找出一个连续子区间包含所有的知识点,求这个子区间的最小长度是多少?
分析:
首先想到的是枚举每个点作为左端点,利用尺取法找到其右端点。考虑用vis数组标记已经学习过的知识点,然后我们在向右搜索时就能知道什么知识点我们还没学习过。用pos数组标记 i 知识点我们是在哪一点学习的,这样当我们左端点右移时,就知道之前的左端点对应的知识点是否唯一。因为对于每个知识点,我们不关心它的值是多少,只需要考虑两个知识点是否为相同的知识点,所以我们就可以把所有数据离散化。
代码:
#include <map> #include <math.h> #include <string> #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define ll long long #define ull unsigned long long #define cls(x) memset(x,0,sizeof(x)) #define clslow(x) memset(x,-1,sizeof(x)) const int maxn=1e6+100; int n; bool vis[maxn]; int a[maxn],b[maxn],pos[maxn]; int main() { // freopen("in.txt","r",stdin); while(scanf("%d",&n)!=EOF) { cls(vis); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); //一共有多少知识点 int sz=unique(b+1,b+n+1)-b-1; for(int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+n+1,a[i])-b-1; } int s=1,e=1; int cnt=0,ans=n+1; while(true) { //向右移动找到右端点 while(e<=n&&cnt<sz){ pos[a[e]]=e; if(!vis[a[e]]){ cnt++; vis[a[e]]=true; } e++; } if(cnt<sz) break; ans=min(ans,e-s); //如果左端点对应的知识点唯一 if(pos[a[s]]==s){ cnt--; vis[a[s]]=false; } s++; } printf("%d ",ans); } return 0; }