Musical Theme
题目大意:
给定一个数组,求最长重复子串,这两个子串不能重叠。
/* 后缀数组不用说了 首先最长允许重复的子串就是max{height} 不允许重复也就是限制了两字符串之间的距离,可以二分答案来做 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 22222 #define INF (1<<30) using namespace std; int n,wa[maxn],wb[maxn],c[maxn],sa[maxn],rk[maxn],h[maxn],a[maxn],s[maxn]; void SA(int *s,int n,int m){ int *x=wa,*y=wb; for(int i=0;i<m;i++)c[i]=0; for(int i=0;i<n;i++)c[x[i]=s[i]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; int k=1,p=0; while(k<=n){ for(int i=n-k;i<n;i++)y[p++]=i; for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; for(int i=0;i<m;i++)c[i]=0; for(int i=0;i<n;i++)c[x[y[i]]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y);x[sa[0]]=0;p=1; for(int i=1;i<n;i++){ if((y[sa[i-1]]==y[sa[i]])&&((y[sa[i-1]+k]==y[sa[i]+k]&&sa[i]+k<n&&sa[i-1]+k<n)||(sa[i]+k>=n&&sa[i-1]+k>=n)))x[sa[i]]=p-1; else x[sa[i]]=p++; } if(p>=n)break; m=p;p=0;k<<=1; } for(int i=1;i<n;i++)rk[sa[i]]=i; k=0; for(int i=0;i<n-1;h[rk[i++]]=k){ if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k])k++; } } bool check(int mid){ bool flag=0; int mn=INF,mx=-INF; for(int i=2;i<=n;i++){ if(h[i]>=mid){ mn=min(mn,min(sa[i],sa[i-1])); mx=max(mx,max(sa[i],sa[i-1])); if(mx-mn>mid)return 1; } else{mx=-INF;mn=INF;} } return 0; } int main(){ while(1){ scanf("%d",&n); if(n==0)return 0; memset(s,0,sizeof(s)); memset(sa,0,sizeof(sa)); memset(rk,0,sizeof(rk)); memset(h,0,sizeof(h)); for(int i=0;i<n;i++)scanf("%d",&a[i]); n--; for(int i=0;i<n;i++)s[i]=a[i+1]-a[i]+88; s[n]=0; SA(s,n+1,176); int l=0,r=n>>1; while(l<r){ int mid=(l+r+1)>>1; if(check(mid))l=mid; else r=mid-1; } if(l>=4)printf("%d\n",l+1); else puts("0"); } return 0; }