题目描述:
长度为n(<=20000)的整数序列(数值范围1到88),求长度大于等于5且最长的两个不重叠子串,使得其中一个整体加上或减去一个数后与另一个完全相同。输出长度。
题目分析:
设原串为,令,到对应于原串的到
偏移后相同 等价于 差分后的串完全相同。
注意差分后两个串需要间隔一个位置。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 20005
using namespace std;
int b[maxn],ary[4][maxn],h[maxn];
int *sa=ary[0],*rk=ary[1],*nsa=ary[2],*nrk=ary[3];
void build_sa(int n,int m,int *a){
memset(b,0,(max(n,m)+1)<<2),a[0]=a[n+1]=rk[n+1]=0;//!!!
for(int i=1;i<=n;i++) b[a[i]]++;
for(int i=1;i<=m;i++) b[i]+=b[i-1];
for(int i=1;i<=n;i++) sa[b[a[i]]--]=i;
for(int i=1;i<=n;i++) rk[sa[i]]=rk[sa[i-1]]+(a[sa[i-1]]!=a[sa[i]]);
for(int k=1;rk[sa[n]]<n;k<<=1){
for(int i=1;i<=n;i++) b[rk[sa[i]]]=i;
for(int i=n;i>=1;i--) if(sa[i]>k) nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+1;i<=n;i++) nsa[b[rk[i]]--]=i;
for(int i=1;i<=n;i++) nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i-1]]!=rk[nsa[i]]||rk[nsa[i-1]+k]!=rk[nsa[i]+k]);
swap(sa,nsa),swap(rk,nrk);
}
for(int i=1,k=0,j;i<=n;h[rk[i]]=k,i++)
for(k&&(k--),j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
}
int n,m,a[maxn];
bool check(int len){
int mx,mn;
for(int i=1;i<=n;i++)
if(h[i]<len) mx=mn=sa[i];
else if((mx=max(mx,sa[i]))-(mn=min(mn,sa[i]))>len) return 1;
return 0;
}
int main()
{
while(scanf("%d",&n),n){
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=n-1;i>0;i--) a[i]=a[i]-a[i-1]+88;
build_sa(--n,175,a);
int l=3,r=n>>1,mid;
for(;l<r;) mid=(l+r+1)>>1,check(mid)?(l=mid):(r=mid-1);
printf("%d\n",l>=4?l+1:0);
}
}
来源:CSDN
作者:Master.Yi
链接:https://blog.csdn.net/C20181220_xiang_m_y/article/details/104022401