Description
农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。
Input
* Line 1: 两个整数 N,K。
* Lines 2..N+1: 每行一个整数表示当天的质量值。
Output
* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度
Sample Input
8 2
1
2
3
2
3
2
3
1
1
2
3
2
3
2
3
1
Sample Output
4
也是SA
height[i]表示字典序排第i的串和字典序排i-1的串的最长公共前缀
然后有一个结论是height[i]>=height[i-1]-1
而且按照后缀的下标排序的话可以让复杂度降到O(n)
具体见2009罗穗骞论文《后缀数组——处理字符串的有力工具》
这题求最长可重复前缀,注意到一旦height[i]<k,那么不可能有一个满足条件的子串同时包含i和i-1
二分答案x,然后统计height[i]>=x的最长连续的一段,如果超过m就可行
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define N 40010 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int srt[N],a[N],hash[N],h[N],ht[N]; int sa[2][N],rnk[2][N],v[N]; int n,m,k,tt,p,q,ans; inline void calc_sa(int sa[N],int rnk[N],int SA[N],int RNK[N]) { for (int i=1;i<=n;i++)v[rnk[sa[i]]]=i; for (int i=n;i>=1;i--)if (sa[i]>k)SA[v[rnk[sa[i]-k]]--]=sa[i]-k; for (int i=n-k+1;i<=n;i++)SA[v[rnk[i]]]=i; for (int i=1;i<=n;i++) RNK[SA[i]]=RNK[SA[i-1]]+(rnk[SA[i]]!=rnk[SA[i-1]]||rnk[SA[i]+k]!=rnk[SA[i-1]+k]); } inline void get_sa() { p=0;q=1;a[0]=-1; memset(v,0,sizeof(v)); for (int i=1;i<=n;i++)v[a[i]]++; for (int i=1;i<=n;i++)v[i]+=v[i-1]; for (int i=1;i<=n;i++)sa[p][v[a[i]]--]=i; for (int i=1;i<=n;i++) rnk[p][sa[p][i]]=rnk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]); for (k=1;k<n;k<<=1,swap(p,q)) calc_sa(sa[p],rnk[p],sa[q],rnk[q]); } inline void get_h() { k=0; for(int i=1;i<=n;i++) { if (rnk[p][i]==1)h[i]=0; else { int j=sa[p][rnk[p][i]-1]; while (a[i+k]==a[j+k])k++; h[i]=k;if (k)k--; } } for (int i=1;i<=n;i++) ht[rnk[p][i]]=h[i]; } inline bool jud(int x) { int temp=0; for (int i=1;i<=n;i++) { if (ht[i]>=x){temp++;if (temp==m-1)return 1;} else temp=0; } return 0; } int main() { n=read();m=read(); for (int i=1;i<=n;i++)srt[i]=a[i]=read(); sort(srt+1,srt+n+1);srt[0]=-1; hash[++tt]=srt[1]; for (int i=2;i<=n;i++)if (srt[i]!=srt[i-1])hash[++tt]=srt[i]; for (int i=1;i<=n;i++)a[i]=lower_bound(hash+1,hash+tt+1,a[i])-hash; get_sa(); get_h(); int l=1,r=n; while (l<=r) { int mid=(l+r)>>1; if (jud(mid)){ans=mid;l=mid+1;} else r=mid-1; } printf("%d ",ans); return 0; }