这个地址上的题解已经好详细了
http://wenku.baidu.com/view/87f5b7f80242a8956bece4df.html
不过本人觉得,加上这部分题解的话,会更好理解
若在假设重复子串的长度最多为L的限制下有解, 则对于任意一个比L小的限制L'<L, 也一定有解. 这就说明存在解的连续性, 这样就可以用二分查找答案长度L.给出一个关于LCP的定理LCP(SA[i], SA[j]) = RMQ(Height[i+1..j]). 由此, 若存在k, 满足Height[k] < L, 则对于所有i, j 满足i < k < j, 有LCP(SA[i], SA[j]) < L. 即公共长度至少为L的两个后缀, 不会跨过一个小于L的Height低谷k, 所以我们可以得到一些由这些低谷划分开的连续的段.
在某段内, 若存在i, j 满足SA[i]+L<SA[j], 则存在一个长度至少为L的2个相同不交迭子串. 实现时只要记录在每段内, 最大和最小的SA值即可.
--Amber大牛,《男人不容易系列Solution》
#include <iostream> using namespace std; #define MAXN 20010 int a[MAXN],b[MAXN],array[4][MAXN],*sa,*nsa,*rank1,*nrank,height[MAXN],n; void make_sa(){ int i,k; sa=array[0]; nsa=array[1]; rank1=array[2]; nrank=array[3]; memset(b,0,sizeof(b)); for(i=0;i<n;i++) b[a[i]]++; for(i=1;i<=256;i++) b[i]+=b[i-1]; for(i=n-1;i>=0;i--) sa[--b[a[i]]]=i; for(rank1[sa[0]]=0,i=1;i<n;i++){ rank1[sa[i]]=rank1[sa[i-1]]; if(a[sa[i]]!=a[sa[i-1]]) rank1[sa[i]]++; } for(k=1;k<n && rank1[sa[n-1]]<n-1;k*=2){ for(i=0;i<n;i++) b[rank1[sa[i]]]=i; for(i=n-1;i>=0;i--) if(sa[i]-k>=0) nsa[b[rank1[sa[i]-k]]--]=sa[i]-k; for(i=n-k;i<n;i++) nsa[b[rank1[i]]--]=i; for(nrank[nsa[0]]=0,i=1;i<n;i++){ nrank[nsa[i]]=nrank[nsa[i-1]]; if(rank1[nsa[i]]!=rank1[nsa[i-1]] || rank1[nsa[i]+k]!=rank1[nsa[i-1]+k]) nrank[nsa[i]]++; } int *t=sa;sa=nsa;nsa=t; t=rank1;rank1=nrank;nrank=t; } } void cal_height(){ int i,j,k; for(k=0,i=0;i<n;i++){ if(rank1[i]==0) height[rank1[i]]=0; else{ for(j=sa[rank1[i]-1];a[i+k]==a[j+k];k++); height[rank1[i]]=k; if(k>0) k--; } } } bool OK(int len){ int i,mn,mx; mn=n; mx=0; for(i=1;i<n;i++){ if(height[i]<len){ mn=n; mx=0; } else{ if(sa[i]>mx) mx=sa[i]; if(sa[i]<mn) mn=sa[i]; if(sa[i-1]>mx) mx=sa[i-1]; if(sa[i-1]<mn) mn=sa[i-1]; if(mx-mn>=len) return true; } } return false; } int main(){ int i,ans,l,r,len; while(scanf("%d",&n) && n){ for(i=0;i<n;i++) scanf("%d",&a[i]); for(i=1;i<n;i++) a[i-1]=a[i]-a[i-1]+88; n--; a[n++]=0; make_sa(); cal_height(); ans=0; l=0;r=n-1; while(l<=r){ len=(l+r)/2; if(OK(len)){ ans=len; l=len+1; } else r=len-1; } if(ans>=4) printf("%d\n",ans+1); else printf("0\n"); } return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> int const N= 20100; int wa[N], wb[N], ws[N], wv[N]; int rank[N], sa[N], height[N], r[N], n, k; int cmp( int* r, int a, int b, int L ){ return r[a]== r[b] && r[a+ L]== r[b+ L]; } void da( int* r, int* sa, int n, int m ){ int i, j, p, *x= wa, *y= wb, *t; for( i= 0; i< m; ++i ) ws[i]= 0; for( i= 0; i< n; ++i ) ws[ x[i]= r[i] ]++; for( i= 1; i< m; ++i ) ws[i]+= ws[i-1]; for( i= n- 1; i>= 0; i-- ) sa[ --ws[ x[i] ] ]= i; for( j= 1, p= 1; p< n; j*= 2, m= p ){ for( p= 0, i= n- j; i< n; ++i ) y[p++]= i; for( i= 0; i< n; ++i ) if( sa[i]>= j ) y[p++]= sa[i]- j; for( i= 0; i< n; ++i ) wv[i]= x[y[i]]; for( i= 0; i< m; ++i ) ws[i]= 0; for( i= 0; i< n; ++i ) ws[ wv[i] ]++; for( i= 1; i< m; ++i ) ws[i]+= ws[i-1]; for( i= n- 1; i>= 0; i-- ) sa[ --ws[ wv[i] ] ]= y[i]; t= x, x= y, y= t, p= 1; x[ sa[0] ]= 0; for( i= 1; i< n; ++i ) x[ sa[i] ]= cmp( y, sa[i-1], sa[i], j )? p- 1: p++; } } void callheight( int* r, int*sa, int n ){ int i, j, k= 0; for( i= 1; i<= n; ++i ) rank[ sa[i] ]= i; for( i= 0; i< n; height[ rank[i++] ]= k ) for( k?k--:0, j= sa[ rank[i]- 1]; r[i+k]== r[j+k]; k++ ); } bool OK(int len) { int mx=0,mi=n; for(int i=1;i<n;i++) { if(height[i]<len) { mx=0;mi=n; } else { if(sa[i]>mx) mx=sa[i]; if(sa[i-1]>mx) mx=sa[i-1]; if(sa[i]<mi) mi=sa[i]; if(sa[i-1]<mi) mi=sa[i-1]; if(mx-mi>=len) return true; } } return false; } int main(){ int i,ans,l,len,right; while(scanf("%d",&n) && n){ for(i=0;i<n;i++) scanf("%d",&r[i]); for(i=1;i<n;i++) r[i-1]=r[i]-r[i-1]+88; n--; r[n++]=0; da(r,sa,n+1,180);//最后一个参数表示r[]数组的最大值,数组ws[]的大小随最后一个参数而变 callheight(r,sa,n); ans=0; l=0;right=n-1; while(l<=right){ len=(l+right)/2; if(OK(len)){ ans=len; l=len+1; } else right=len-1; } if(ans>=4) printf("%d\n",ans+1); else printf("0\n"); } return 0; }