poj 3261 Milk Patterns(后缀数组)(k次的最长重复子串)
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 7938 | Accepted: 3598 | |
Case Time Limit: 2000MS |
Description
Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular patterns in the daily milk quality.
To perform a rigorous study, he has invented a complex classification scheme by which each milk sample is recorded as an integer between 0 and 1,000,000 inclusive, and has recorded data from a single cow overN (1 ≤ N ≤ 20,000) days. He wishes to find the longest pattern of samples which repeats identically at least K (2 ≤ K ≤ N) times. This may include overlapping patterns -- 1 2 3 2 3 2 3 1 repeats 2 3 2 3 twice, for example.
Help Farmer John by finding the longest repeating subsequence in the sequence of samples. It is guaranteed that at least one subsequence is repeated at least K times.
Input
Output
Sample Input
8 2 1 2 3 2 3 2 3 1
Sample Output
4
给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠。
算法分析:这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。
下面给一组数据:5 2 1 1 1 1 1 一组数据发现RE bug
先贴个RE代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include<string.h> 4 5 using namespace std; 6 7 #define maxn 1100000 8 9 #define cls(x) memset(x, 0, sizeof(x)) 10 11 int wa[maxn],wb[maxn],wv[maxn],wss[maxn]; 12 13 int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 14 15 //倍增算法 16 void da(int *r,int *sa,int n,int m) 17 { 18 cls(wa); 19 cls(wb); 20 cls(wv); 21 int i,j,p,*x=wa,*y=wb,*t; 22 23 //基数排序 24 for(i=0;i<m;i++) wss[i]=0; 25 for(i=0;i<n;i++) wss[x[i]=r[i]]++; 26 for(i=1;i<m;i++) wss[i]+=wss[i-1]; 27 for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i; 28 29 // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。 30 for(j=1,p=1;p<n;j*=2,m=p) 31 { 32 //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次 33 for(p=0,i=n-j;i<n;i++) y[p++]=i; 34 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 35 36 //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序, 37 for(i=0;i<n;i++) wv[i]=x[y[i]]; 38 for(i=0;i<m;i++) wss[i]=0; 39 for(i=0;i<n;i++) wss[wv[i]]++; 40 for(i=1;i<m;i++) wss[i]+=wss[i-1]; 41 for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i]; 42 43 //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。 44 for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) 45 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 46 } 47 } 48 49 int rank[maxn],height[maxn]; 50 51 //得到height数组:排名相邻的两个后缀的最长公共前缀 52 void calheight(int *r,int *sa,int n) 53 { 54 cls(rank); 55 cls(height); 56 int i,j,k=0; 57 for(i=1;i<n;i++) rank[sa[i]]=i; 58 for(i=0;i<n;height[rank[i++]]=k) 59 for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 60 return; 61 } 62 63 int ca[maxn]; 64 int sa[maxn]; 65 int N; 66 67 int isok(int m,int k){ 68 int sum=0,i; 69 for(i=1;i<N;i++){ 70 if(height[i]>=m){ 71 sum++; 72 if(sum>=k-1){ 73 return 1; 74 } 75 }else{ 76 sum=0; 77 } 78 } 79 return 0; 80 } 81 82 int main() 83 { 84 int k,i; 85 while (~scanf("%d%d",&N,&k)) 86 { 87 for(i=0;i<N;i++) 88 scanf("%d",&ca[i]); 89 da(ca, sa, N, maxn); 90 calheight(ca,sa,N); 91 92 // for(i=0;i<N;i++) printf("%d ",height[i]); putchar(10); 93 int j; 94 i=0; 95 j=N; 96 while(i<=j){ 97 int mid = (i+j)/2; 98 if(isok(mid,k)){ 99 i=mid+1; 100 }else{ 101 j=mid-1; 102 } 103 } 104 printf("%d\n",j); 105 } 106 return 0; 107 }
再来一个AC码:(根据RE代码改过来的)
1 #include <iostream> 2 #include <stdio.h> 3 #include<string.h> 4 5 using namespace std; 6 7 #define maxn 1100000 8 9 #define cls(x) memset(x, 0, sizeof(x)) 10 11 int wa[maxn],wb[maxn],wv[maxn],wss[maxn]; 12 13 int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 14 15 //倍增算法 16 void da(int *r,int *sa,int n,int m) 17 { 18 cls(wa); 19 cls(wb); 20 cls(wv); 21 int i,j,p,*x=wa,*y=wb,*t; 22 23 //基数排序 24 for(i=0;i<m;i++) wss[i]=0; 25 for(i=0;i<n;i++) wss[x[i]=r[i]]++; 26 for(i=1;i<m;i++) wss[i]+=wss[i-1]; 27 for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i; 28 29 // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。 30 for(j=1,p=1;p<n;j*=2,m=p) 31 { 32 //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次 33 for(p=0,i=n-j;i<n;i++) y[p++]=i; 34 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 35 36 //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序, 37 for(i=0;i<n;i++) wv[i]=x[y[i]]; 38 for(i=0;i<m;i++) wss[i]=0; 39 for(i=0;i<n;i++) wss[wv[i]]++; 40 for(i=1;i<m;i++) wss[i]+=wss[i-1]; 41 for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i]; 42 43 //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。 44 for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) 45 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 46 } 47 } 48 49 int rank[maxn],height[maxn]; 50 51 //得到height数组:排名相邻的两个后缀的最长公共前缀 52 void calheight(int *r,int *sa,int n) 53 { 54 cls(rank); 55 cls(height); 56 int i,j,k=0; 57 58 for(i=1;i<n;i++) rank[sa[i]]=i; 59 // for(i=0;i<n;i++) printf("%d ",rank[i]); putchar(10); system("pause"); 60 61 for(i=0;i<n;height[rank[i++]]=k) 62 for(k?k--:0,j=sa[((rank[i]-1<0)?0:rank[i]-1)];r[i+k]==r[j+k]&&i!=j;k++); // 加上r[i+k]==r[j+k] => r[i+k]==r[j+k]&&i!=j 63 return; 64 } 65 66 int ca[maxn]; 67 int sa[maxn]; 68 int N; 69 70 int isok(int m,int k){ 71 int sum=0,i; 72 for(i=1;i<N;i++){ 73 if(height[i]>=m){ 74 sum++; 75 if(sum>=k-1){ 76 return 1; 77 } 78 }else{ 79 sum=0; 80 } 81 } 82 return 0; 83 } 84 85 int main() 86 { 87 int k,i; 88 while (~scanf("%d%d",&N,&k)) 89 { 90 for(i=0;i<N;i++) 91 scanf("%d",&ca[i]); 92 ca[N] = 1000000; //加上一个结尾,排除数字全相同的情况 93 N++; 94 da(ca, sa, N, maxn-1); 95 calheight(ca,sa,N); 96 97 int j; 98 i=0; 99 j=N; 100 while(i<=j){ 101 int mid = (i+j)/2; 102 if(isok(mid,k)){ 103 i=mid+1; 104 }else{ 105 j=mid-1; 106 } 107 } 108 printf("%d\n",j); 109 } 110 return 0; 111 }
后缀数组第二种实现方式:AC码:
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #define MAXN 500010 5 6 char str[MAXN*2]; 7 int s[MAXN*2],sa[MAXN*2],rank[MAXN*2],trank[MAXN*4],sum[MAXN*4],tsa[MAXN*2],height[MAXN*2]; 8 9 void sorting(int j,int len)//基数排序 10 { 11 int i; 12 memset(sum,0,sizeof(sum)); 13 for (i=1; i<=len; i++) sum[ rank[i+j] ]++; 14 for (i=1; i<=len; i++) sum[i]+=sum[i-1]; 15 for (i=len; i>0; i--) tsa[ sum[ rank[i+j] ]-- ]=i;//对第二关键字计数排序,tsa代替sa为排名为i的后缀是tsa[i] 16 17 memset(sum,0,sizeof(sum)); 18 for (i=1; i<=len; i++) sum[ rank[i] ]++; 19 for (i=1; i<=len; i++) sum[i]+=sum[i-1]; 20 for (i=len; i>0; i--) sa[ sum[ rank[ tsa[i] ] ]-- ]= tsa[i]; //对第一关键字计数排序 21 //构造互逆关系 22 // for(i=1;i<=len;i++) printf("%d ",rank[i]); putchar(10); 23 } 24 25 void getsa(int len){ 26 27 memset(sum,0,sizeof(sum)); 28 memset(rank,0,sizeof(rank)); 29 memset(height,0,sizeof(height)); 30 memset(trank,0,sizeof(trank)); 31 memset(sa,0,sizeof(sa)); 32 memset(tsa,0,sizeof(tsa)); 33 34 int i; 35 for (i=0; i<len; i++) { 36 trank[i+1]=s[i]; 37 } 38 for (i=1; i<=len; i++) { 39 sum[ trank[i] ]++; 40 } 41 for (i=1; i<=40; i++) sum[i]+=sum[i-1]; 42 for (i=len; i>0; i--) sa[ sum[ trank[i] ]-- ]=i; 43 44 rank[ sa[1] ]=1; 45 46 int p; 47 for (i=2,p=1; i<=len; i++) 48 { 49 if (trank[ sa[i] ]!=trank[ sa[i-1] ]) p++; 50 rank[ sa[i] ]=p; 51 }//第一次的sa与rank构造完成 52 53 for (int j=1; j<=len; j*=2) 54 { 55 sorting(j,len); 56 trank[ sa[1] ]=1; 57 p=1; //用trank代替rank 58 for (i=2; i<=len; i++) 59 { 60 if ((rank[ sa[i] ]!=rank[ sa[i-1] ]) || (rank[ sa[i]+j ]!=rank[ sa[i-1]+j ])) p++; 61 trank[ sa[i] ]=p;//空间要开大一点,至少2倍 62 } 63 for (i=1; i<=len; i++) rank[i]=trank[i]; 64 } 65 } 66 67 void getheight(int len) 68 { 69 for (int i=1,j=0; i<=len; i++)//用j代替上面的h数组 70 { 71 if (rank[i]==1) continue; 72 for (; s[i+j-1]==s[ sa[ rank[i]-1 ]+j-1 ]; ) j++;//注意越界之类的问题 73 height[ rank[i] ]=j; 74 if (j>0) j--; 75 } 76 } 77 int N; 78 79 int isok(int m,int k){ 80 int sum=0,i; 81 for(i=2;i<=N;i++){ 82 if(height[i]>=m){ 83 sum++; 84 if(sum>=k-1){ 85 return 1; 86 } 87 }else{ 88 sum=0; 89 } 90 } 91 return 0; 92 } 93 94 int main() 95 { 96 int k,i; 97 while (~scanf("%d%d",&N,&k)) 98 { 99 for(i=0;i<N;i++) 100 scanf("%d",&s[i]); 101 getsa(N); 102 getheight(N); 103 104 int j; 105 i=0; 106 j=N; 107 while(i<=j){ 108 int mid = (i+j)/2; 109 if(isok(mid,k)){ 110 i=mid+1; 111 }else{ 112 j=mid-1; 113 } 114 } 115 printf("%d\n",j); 116 } 117 return 0; 118 }