P2852 [USACO06DEC]牛奶模式Milk Patterns
//Pro: P2852 [USACO06DEC]牛奶模式Milk Patterns //后缀数组 //求出现次数>=k的最长可重叠字串的长度 //首先出现次数为k+1的串的长度肯定不大于出现次数为k的串 //所以我们就求出现次数为k次的串 //我们把这个串看成是原串的后缀的前缀 //那么这个串就是k个后缀的前缀 //而且这k个后缀的rnk是连续的 //又因为height[i]记录的是sa[i]与sa[i-1]的lcp //所以我们找k-1个连续的height,就找到了rnk连续的k个后缀 //而这k个后缀的height的最小值就是一个满足“出现次数>=k次”的子串的长度 //我们要求的就是所有连续k-1个height的最小值的最大值 //单调队列维护一下 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; inline int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } const int N=2e4+5; int n,k,m; int a[N],b[N]; int rnk[N],sa[N],tp[N],tax[N],height[N]; inline void Qsort() { for(int i=1;i<=m;++i) tax[i]=0; for(int i=1;i<=n;++i) ++tax[rnk[i]]; for(int i=1;i<=m;++i) tax[i]+=tax[i-1]; for(int i=n;i;--i) sa[tax[rnk[tp[i]]]--]=tp[i]; } void Suffix_sort() { for(int i=1;i<=n;++i) rnk[i]=a[i],tp[i]=i; Qsort(); for(int l=1,p=0;p<n;m=p,l<<=1) { p=0; for(int i=n-l+1;i<=n;++i) tp[++p]=i; for(int i=1;i<=n;++i) if(sa[i]>l) tp[++p]=sa[i]-l; Qsort(); swap(tp,rnk); rnk[sa[1]]=p=1; for(int i=2;i<=n;++i) rnk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+l]==tp[sa[i-1]+l])?p:++p; } } void Get_height() { int j,k=0; for(int i=1;i<=n;++i) { if(k) --k; j=sa[rnk[i]-1]; while(a[i+k]==a[j+k]) ++k; height[rnk[i]]=k; } } int que[N],head,tail; int main() { n=read(),k=read(); for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i]; sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b; for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+m,a[i])-b; Suffix_sort(); Get_height(); int ans=0; head=1; for(int i=1;i<=n;++i) { while(head<=tail&&que[head]<=i-k+1) ++head; while(head<=tail&&height[que[tail]]>=height[i]) --tail; que[++tail]=i; if(i>=k) ans=max(ans,height[que[head]]); } cout<<ans; return 0; }