http://poj.org/problem?id=1743
后缀数组模板题 要求两个不重叠不相长度相等且相邻两数之差相同的子序列的最长长度 先将原序列相邻两数作差 得到新序列后再将所有后缀按字典序排序 hgt数组即代表字典序相邻的两后缀的lcp长度 二分一个长度mid 然后扫一遍hgt数组 拥有相同长为mid的lcp的两个后缀一定挨在一起 找出这练成一片的后缀里最大下标和最小下标作差即为所求
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e4+10;
const int N=0x3f3f3f3f;
int ary[maxn],rnk[maxn],hgt[maxn],t1[maxn],t2[maxn],sum[maxn],sa[maxn];
void getsa(int *r,int *sa,int n,int m)
{
int *x,*y,*t;
int i,j,p;
x=t1,y=t2;
for(i=0;i<m;i++) sum[i]=0;
for(i=0;i<n;i++) sum[x[i]=r[i]]++;
for(i=1;i<m;i++) sum[i]+=sum[i-1];
for(i=n-1;i>=0;i--) sa[--sum[x[i]]]=i;
for(p=1,j=1;p<n;j*=2)
{
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<m;i++) sum[i]=0;
for(i=0;i<n;i++) sum[x[y[i]]]++;
for(i=1;i<m;i++) sum[i]+=sum[i-1];
for(i=n-1;i>=0;i--) sa[--sum[x[y[i]]]]=y[i];
t=x,x=y,y=t;
x[sa[0]]=0;
for(p=1,i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
m=p;
}
return;
}
void getht(int *r,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k) k--;
else k=0;
j=sa[rnk[i]-1];
while(r[i+k]==r[j+k]) k++;
hgt[rnk[i]]=k;
}
return;
}
bool judge(int k,int n)
{
int maxx,minn,i;
maxx=-N,minn=N;
for(i=2;i<=n;i++)
{
if(hgt[i]>=k)
{
maxx=max(maxx,max(sa[i-1],sa[i]));
minn=min(minn,min(sa[i-1],sa[i]));
if(maxx-minn>k) return true;
}
else maxx=-N,minn=N;
}
return false;
}
int main()
{
int t,n,i,l,r,m,ans;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
for(i=0;i<n;i++) scanf("%d",&ary[i]);
n--;
for(i=0;i<n;i++) ary[i]=ary[i+1]-ary[i]+88;
ary[n]=0;
getsa(ary,sa,n+1,256);
getht(ary,n);
l=0,r=n/2,ans=0;
while(l<=r)
{
m=(l+r)/2;
if(judge(m,n)) l=m+1,ans=m;
else r=m-1;
}
if(ans+1>=5) printf("%d\n",ans+1);
else printf("0\n");
}
return 0;
}