poj1743 Musical Theme
【题意】
找出最长的相似不重叠子串,这里相似定义为两个串每次字符对应的差值相同
【分析】
显然,我们可以首先讲相邻两个的差值作为新的字符串来比较,这样原问题就转换为了求最长的不重叠重复子串
先利用二分,转换为判定性问题,然后对height进行分组,大于等于mid的可以分在一组,如果这一组内的最大和最小的位置差大于等于长度,也就是说这两个串是错开的,那么就可以返回存在
【代码】
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e5+5; int n; int sa[maxn],rk[maxn],h[maxn]; int cnt[maxn],fz[maxn],a[maxn],id[maxn],oldrk[maxn]; bool cmp(int x,int y,int w) { return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w]; } void calcsa() { int m=233,p,i; for(i=0;i<=m;i++) cnt[i]=0; for(i=1;i<=n;i++) cnt[rk[i]=a[i]]++; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i; for(int w=1;;w<<=1,m=p) { for(p=0,i=1;i<=w;i++) id[++p]=n-i+1; for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w; for(i=0;i<=m;i++) cnt[i]=0; for(i=1;i<=n;i++) cnt[fz[i]=rk[id[i]]]++; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[fz[i]]--]=id[i]; memcpy(oldrk,rk,sizeof(rk)); rk[sa[1]]=p=1; for(i=2;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p; if(p==n) break; } } void calch() { int i,k; for(i=1,k=0;i<=n;i++) { if(k) k--; while(a[i+k]==a[sa[rk[i]-1]+k]) k++; h[rk[i]]=k; } } bool check(int x) { int maxx,minn; maxx=minn=sa[1]; for(int i=2;i<=n;i++) { if(h[i]>=x-1) { minn=min(minn,sa[i]); maxx=max(maxx,sa[i]); } else minn=maxx=sa[i]; if(maxx-minn>=x) return 1; } return 0; } int main() { freopen("piano.in","r",stdin); freopen("piano.out","w",stdout); while(scanf("%d",&n)!=EOF && n) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=n;i>=1;i--) a[i]-=a[i-1]-100; calcsa(); calch(); int l=0,r=n,ans; while(l<=r) { int mid=l+r>>1; if(check(mid)) l=mid+1,ans=mid; else r=mid-1; } printf("%d\n",ans>=5?ans:0); } return 0; }