其实和上一题是差不多的,只是在二分check的时候有一些小小的改动
1468: 后缀数组2:可重叠的k次最长重复子串
时间限制: 1 Sec 内存限制: 128 MB
提交: 113 解决: 48
[提交] [状态] [讨论版] [命题人:admin]题目描述
【问题描述】
农夫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。(可重叠的k次最长重复子串)
【输入格式】
* Line 1: 两个整数 N,K。
* Lines 2..N+1: 每行一个整数表示当天的质量值。
(多组数据)
【输出格式】
* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度
【样例】
输入:8 2
1
2
3
2
3
2
3
1
输出:4
做法是是和上一题:不可重叠的最长重复子串是差不多的,就是要判断后缀分成若干组,然后判断有没有一个组后缀个数是不小于k的,存在就满足,不存在就不满足
代码实现,没什么注释,所以我就只放一个版本
1 /*后将后缀分成若干组。 不同的是,这里要判断的是有没有一个组的后缀个数不小于 k。 2 如果有,那么存在k 个相同的子串满足条件,否则不存在*/ 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<cmath> 8 #include<iostream> 9 using namespace std; 10 int sa[1000010],Rank[1000010],rsort[1000010]; 11 int a[1000010],cnt[1000010],pos[100010],height[1000010]; 12 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];} 13 void get_sa(int n,int m) 14 { 15 int k=1,p=0,len; 16 for(int i=1;i<=n;i++) Rank[i]=a[i]; 17 memset(rsort,0,sizeof(rsort)); 18 for(int i=1;i<=n;i++) rsort[Rank[i]]++; 19 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 20 for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i; 21 for(int k=1;k<n;k<<=1) 22 { 23 len=0; 24 for(int i=n-k+1;i<=n;i++) pos[++len]=i; 25 for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k; 26 for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]]; 27 memset(rsort,0,sizeof(rsort)); 28 for(int i=1;i<=n;i++) rsort[cnt[i]]++; 29 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 30 for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i]; 31 for(int i=1;i<=n;i++) cnt[i]=Rank[i]; 32 p=1; Rank[sa[1]]=1; 33 for(int i=2;i<=n;i++) 34 { 35 if(!cmp(sa[i],sa[i-1],k)) p++; 36 Rank[sa[i]]=p; 37 } 38 if(p==n) break; m=p; 39 } 40 a[0]=0; sa[0]=0; 41 } 42 void get_he(int n) 43 { 44 int j,k=0; 45 for(int i=1;i<=n;i++) 46 { 47 j=sa[Rank[i]-1]; 48 if(k) k--; 49 while(a[j+k]==a[i+k]) k++; 50 height[Rank[i]]=k; 51 } 52 } 53 bool check(int mid,int n,int k) 54 { 55 int sum=1; 56 for(int i=2;i<=n;i++) 57 { 58 if(height[i]>=mid)/*满足条件*/ 59 { 60 sum++;/*个数增加*/ 61 if(sum==k) return true;/*如果满足条件就直接返回*/ 62 } 63 else sum=1;/*否则重新找*/ 64 } 65 return false; 66 } 67 int main() 68 { 69 int n,k; 70 while(scanf("%d%d",&n,&k)!=EOF) 71 { 72 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 73 get_sa(n,1000010); get_he(n); 74 int l=0,r=n,ans=0; 75 while(l<=r) 76 { 77 int mid=(l+r)/2; 78 if(check(mid,n,k)) 79 { 80 ans=mid; 81 l=mid+1; 82 } 83 else r=mid-1; 84 } 85 printf("%d ",ans); 86 } 87 return 0; 88 }