原问题,其实是找最长的相似子串,所谓相似就是一个子串每个值加上一个偏移值可以得到另一个子串。
我们先求原数组的差值数组,对新数组求后缀数组,二分答案,判定是否有某个Height数组中的sa最小值与最大值之差大于当前枚举的子串长度。
#include <iostream> #include <vector> #include <algorithm> #include <string> #include <string.h> #include <stdio.h> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> #include <ctime> #include <cassert> #include <sstream> using namespace std; const int N=2e6+10010; int sa[N]; int t1[N],t2[N],c[N]; int rk[N],height[N]; inline int cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } int s[N]; void calcSA (int *s,int n,int m) { int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=s[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1){ p=0; for(i=n-j;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; // 排名从小到大,如果pos比j大,则suffix(sa[i]-j)的第二关键字为p for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[y[i]]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; // 根据第二关键字从大到小,确定新一轮sa swap(x,y); 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++; if(p>=n)break; m=p; } } void calcHeight(int *s,int n) { int i,j,k=0; for(i=0;i<=n;i++)rk[sa[i]]=i; for(i=0;i<n;i++){ if(k)k--; // h[i]>=h[i-1]-1 j=sa[rk[i]-1]; // suffix(j)排名在suffix(i)前一位 while(s[i+k]==s[j+k])k++; // 暴力计算lcp height[rk[i]]=k; } } bool ok(int n,int m) { int mi=sa[1],mx=sa[1]; for (int i=2;i<=n;i++) { if (height[i]<m) { mi=sa[i]; mx=sa[i]; continue; } mi=min(mi,sa[i]); mx=max(mx,sa[i]); if (mx-mi>m) return true; } return false; } int a[N]; int main () { int n; while (scanf("%d",&n)!=EOF,n) { 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]+100; // 保证正数 } s[n]=0; calcSA(s,n+1,200); calcHeight(s,n); int l=4,r=n/2,ret=-1; while (l<=r) { int m=(l+r)>>1; if (ok(n,m)) { ret=m; l=m+1; } else r=m-1; } printf("%d ",ret+1); } return 0; }