【题目链接】
http://poj.org/problem?id=3261
【题意】
至少出现k次的可重叠最长子串。
【思路】
二分长度+划分height,然后判断是否存在一组的数目不小于k即可。
需要注意的是:用末尾添0的方法解决n==1时的RE问题,但是在can中需要忽略height[0]且考虑height[1..n]。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 5 using namespace std; 6 7 const int maxn = 20000+10; 8 const int maxm = 1000000+10; 9 10 int s[maxn]; 11 int sa[maxn],c[maxm],t[maxn],t2[maxn]; 12 13 void build_sa(int m,int n) { 14 int i,*x=t,*y=t2; 15 for(i=0;i<m;i++) c[i]=0; 16 for(i=0;i<n;i++) c[x[i]=s[i]]++; 17 for(i=1;i<m;i++) c[i]+=c[i-1]; 18 for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; 19 20 for(int k=1;k<=n;k<<=1) { 21 int p=0; 22 for(i=n-k;i<n;i++) y[p++]=i; 23 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 24 25 for(i=0;i<m;i++) c[i]=0; 26 for(i=0;i<n;i++) c[x[y[i]]]++; 27 for(i=0;i<m;i++) c[i]+=c[i-1]; 28 for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; 29 30 swap(x,y); 31 p=1; x[sa[0]]=0; 32 for(i=1;i<n;i++) 33 x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; 34 if(p>=n) break; 35 m=p; 36 } 37 } 38 int rank[maxn],height[maxn]; 39 void getHeight(int n) { 40 int i,j,k=0; 41 for(i=0;i<=n;i++) rank[sa[i]]=i; 42 for(i=0;i<n;i++) { 43 if(k) k--; 44 j=sa[rank[i]-1]; 45 while(s[j+k]==s[i+k]) k++; 46 height[rank[i]]=k; 47 } 48 } 49 50 int n,k; 51 52 bool can(int limit) { 53 int cnt=1; //忽略height[0] 截至height[n] 54 for(int i=2;i<=n;i++) { 55 if(height[i]>=limit) { 56 if(++cnt>=k) return true; 57 } 58 else cnt=1; 59 } 60 return false; 61 } 62 63 int main() { 64 scanf("%d%d",&n,&k); 65 int up=0; 66 FOR(i,0,n-1) { 67 scanf("%d",&s[i]); 68 s[i]++; 69 up=max(up,s[i]); 70 } 71 s[n]=0; 72 build_sa(up+1,n); 73 getHeight(n); 74 int L=0,R=n; 75 while(L<R) { 76 int M=L+(R-L+1)/2; 77 if(can(M)) L=M; 78 else R=M-1; 79 } 80 printf("%d ",L); 81 return 0; 82 }