[Usaco2006 Dec]Milk Patterns
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
Sample Output
4
思路:和上一题一样,求最长可重叠的重复子串,但多了一个限制,要至少出现k次。二分,按height分组后统计是否有一个组的后缀个数大于k,如果有,那么就表明这一组的公共前缀在原字符串里出现了k次以上,那么就符合要求,return true;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxl=40010; int sa[maxl],tsa[maxl],rank[maxl],trank[maxl],sum[1000010],n,s[maxl],k,ans,h[maxl]; void sorts(int j){ memset(sum,0,sizeof(sum)); for (int i=1;i<=n;i++) sum[rank[i+j]]++; for (int i=1;i<=n;i++) sum[i]+=sum[i-1]; for (int i=n;i;i--) tsa[sum[rank[i+j]]--]=i; memset(sum,0,sizeof(sum)); for (int i=1;i<=n;i++) sum[rank[tsa[i]]]++; for (int i=1;i<=n;i++) sum[i]+=sum[i-1]; for (int i=n;i;i--) sa[sum[rank[tsa[i]]]--]=tsa[i]; } void getsa(){ for (int i=1;i<=n;i++) trank[i]=s[i]; for (int i=1;i<=n;i++) sum[trank[i]]++;//for (;;); for (int i=1;i<=1000000;i++) sum[i]+=sum[i-1]; for (int i=n;i;i--) sa[sum[trank[i]]--]=i; rank[sa[1]]=1; for (int i=2,p=1;i<=n;i++){ if (trank[sa[i]]!=trank[sa[i-1]]) p++; rank[sa[i]]=p; } for (int j=1;j<=n;j*=2){ sorts(j); trank[sa[1]]=1; for (int i=2,p=1;i<=n;i++){ if (rank[sa[i]]!=rank[sa[i-1]]||rank[sa[i]+j]!=rank[sa[i-1]+j]) p++; trank[sa[i]]=p; } memcpy(rank,trank,sizeof(rank)); } } void geth(){ for (int i=1,j=0;i<=n;i++){ if (rank[i]==1) continue; while (s[i+j]==s[sa[rank[i]-1]+j]) j++; h[rank[i]]=j; if (j) j--; } } bool check(int lim){ int len=1,maxs=0,mins=100000000; for (int i=2;i<=n;i++){ if (h[i]<lim){maxs=0,mins=100000000,len=0;} maxs=max(maxs,sa[i]); mins=min(mins,sa[i]); len++; if (len>=k) return true; } return false; } int main(){ scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%d",&s[i]); getsa(); geth(); int l=1,r=20000,mid=(l+r)>>1; while (l<=r){ if (check(mid)){ l=mid+1; ans=mid; } else r=mid-1; mid=(l+r)>>1; } printf("%d\n",ans); //for (;;); return 0; }