先是论文中的一个模板
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 后缀数组模板 rank[0~n-1] : 值域: 1~n sa[1~n] ; 值域: 0~n-1 height[2~n]: suffix[i-1]与suffix[i]的最长公共前缀 suffix(i-1) 与 suffix(i)是排名相邻的两个后缀 height[1]无意义 */ #include<cstdio> #include<cstring> #include<set> using namespace std; const int maxn = 400010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); return ; } void da(int n,int m=500){//对于每道题目,要注意m的取值,m大于所有出现过的字符 int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; }
ural 1297 最长回文子串 http://acm.timus.ru/problem.aspx?space=1&num=1297
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 给你一个字符串 求一个这个字符串的最长的回文子串 将字符串反转接到原字符串的后面,中间用一个未出现过的字符隔开 然后求后缀间的最长公共前缀即可,需要用到RMQ求区间最值(具体见论文) 注意:两个后缀不同属于一个字符串 rank[0~n-1] : 值域: 1~n sa[0~n-1] ; 值域: 1~n height[2~n]: suffix[i-1]与suffix[i]的最长公共前缀 suffix(i-1) 与 suffix(i)是排名相邻的两个后缀 height[1]无意义 */ #include<cstdio> #include<cstring> const int maxn = 200010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++) ; return ; } void da(int n,int m=130){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } char s[maxn],s1[maxn]; const int M =200100; inline int min(int a,int b){return a<b?a:b;} int dp[20][2*M],LOG[2*M]; void rmq_init(int n,int num[])//num[]下标从1开始 { int i,j; for(j=1;j<=n;j++) dp[0][j]=num[j]; for(j=1;j<=LOG[n];j++) { int limit=n+1-(1<<j); for(i=1;i<=limit;i++) { int x=i+(1<<j>>1); dp[j][i]=min(dp[j-1][x],dp[j-1][i]); } } } int rmq(int l,int r,int num[]){ if(l>r){int t=l;l=r;r=t;} int m=LOG[r-l+1]; return min(dp[m][l],dp[m][r-(1<<m)+1]); } void init(){ LOG[0]=-1; for(int i=1;i<2*M;i++) LOG[i]=LOG[i>>1]+1; } void solve(int n,int len) { int i,k,ans=0,minr=maxn,t; da(n+1);//注意这里 rmq_init(n,height); for(i=0;i<len;i++) { int a=rank[i],b=rank[n-1-i]; if(a>b) {t=a;a=b;b=t;} k=rmq(a+1,b,height); if(2*k-1>ans || (2*k-1==ans&&i-k+1<minr)) ans=2*k-1,minr=i-k+1; if(i==0) continue;//细节处理 : i=0时左边没有字符,不用往下了 a=rank[i],b=rank[n-i]; if(a>b) {t=a;a=b;b=t;} k=rmq(a+1,b,height); if(2*k>ans || (2*k==ans && i-k < minr)) ans=2*k,minr=i-k; } for(i=0;i<ans;i++) printf("%c",r[i+minr]); puts(""); } int main() { int i,j; init(); while(scanf("%s",s)!=EOF) { int len1=strlen(s); for(i=0,j=len1-1;i<len1;i++,j--) s1[i]=s[j]; s1[len1]='\0'; s[len1]='$';s[len1+1]='\0'; strcat(s,s1); int len=len1*2+1; for(i=0;i<len;i++) r[i]=s[i];r[len]=0; solve(len,len1); } return 0; }
数一个字符串中有多少的回文子串
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 受了学长的启发,我随机造了几组大数据。 用求最长回文子串AC的代码稍微修改之后做的,其实数据也只有我一个人验证过,但是应该没错 因为代码基本没怎么改,而且测了很多小的数据都对的,如果有错,请联系我 做法:也是先将字符串反转过来接在原来字符串的后面,中间用一个未出现过的字符隔开 也是先找回文子串,转换为利用RMQ求两个后缀的最长公共前缀 即求下标在rank[a],rank[b]之间的height【】的最小值,就是后缀a和b的最长公共前缀 求出了某个回文子串后,这个子串具有的回文子串也就确定了 分奇偶讨论即可 ababa共有三个 abba共有两个 */ #include<cstdio> #include<cstring> const int maxn = 200010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++) ; return ; } void da(int n,int m=130){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } char s[maxn],s1[maxn]; const int M =200100; inline int min(int a,int b){return a<b?a:b;} int dp[20][2*M],LOG[2*M]; void rmq_init(int n,int num[]) { int i,j; for(j=1;j<=n;j++) dp[0][j]=num[j]; for(j=1;j<=LOG[n];j++) { int limit=n+1-(1<<j); for(i=1;i<=limit;i++) { int x=i+(1<<j>>1); dp[j][i]=min(dp[j-1][x],dp[j-1][i]); } } } int rmq(int l,int r,int num[]){ if(l>r){int t=l;l=r;r=t;} int m=LOG[r-l+1]; return min(dp[m][l],dp[m][r-(1<<m)+1]); } void init(){ LOG[0]=-1; for(int i=1;i<2*M;i++) LOG[i]=LOG[i>>1]+1; } void solve(int n,int len) { int i,k,ans=0,t; da(n+1); rmq_init(n,height); for(i=0;i<len;i++) { int a=rank[i],b=rank[n-1-i]; if(a>b) {t=a;a=b;b=t;} k=rmq(a+1,b,height); if(k>1) ans+=k-1; if(i==0) continue; a=rank[i],b=rank[n-i]; if(a>b) {t=a;a=b;b=t;} k=rmq(a+1,b,height); if(k>0) ans+=k; } printf("%d\n",ans); } int main() { int i,j; init(); while(scanf("%s",s)!=EOF) { int len1=strlen(s); for(i=0,j=len1-1;i<len1;i++,j--) s1[i]=s[j]; s1[len1]='\0'; s[len1]='$';s[len1+1]='\0'; strcat(s,s1); int len=len1*2+1; for(i=0;i<len;i++) r[i]=s[i];r[len]=0; solve(len,len1); } return 0; }
poj 2774 求两个字符串的最长公共子串
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 两个字符串的最长公共子串 直接把一个字符串拼接到另一个字符串后面,中间用一个未出现过的字符隔开 然后求后缀间的最长公共前缀,注意,所有的height[]的最大值不一定就是答案 当suffix(sa[i-1])和suffix(sa[i])(排名相邻的两个后缀) 不是同一个字符串中的两个后缀时,height[i]才满足条件 */ #include<cstdio> #include<cstring> const int maxn = 200010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++) ; return ; } void da(int n,int m=130){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } bool check(int i,int n) { int a=0,b=0; if(sa[i]<=n) a=1; if(sa[i-1]<=n) b=1; return (a!=b); } char s2[maxn],s1[maxn]; int main() { int i,j; while(scanf("%s%s",s1,s2)!=EOF) { int len1=strlen(s1); int len2=strlen(s2); int len=len1+len2+1; s1[len1]='$';s1[len1+1]='\0'; strcat(s1,s2); for(i=0;i<len;i++) r[i]=s1[i]; da(len+1); int max=0; for(i=2;i<=len;i++) { if(height[i]>max && check(i,len1)) max=height[i]; } printf("%d\n",max); } return 0; }
poj 3261 求可重叠k次的最长重复串
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 后缀数组模板 rank[0~n-1] : 值域: 1~n sa[0~n-1] ; 值域: 1~n height[2~n]: suffix[i-1]与suffix[i]的最长公共前缀 suffix(i-1) 与 suffix(i)是排名相邻的两个后缀 height[1]无意义 */ #include<cstdio> #include<cstring> const int maxn = 200010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); return ; } void da(int n,int m=200){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } bool solve(int mid,int n,int k) { da(n+1);int cnt=1; for(int i=2;i<=n;i++) { if(height[i]>=mid) { cnt++; if(cnt==k) return true; } else cnt=1; } return false; } int main(){ int n,k,i,j; while(scanf("%d%d",&n,&k)!=EOF){ for(i=0;i<n;i++) scanf("%d",&r[i]); int l=1,r=n-1,mid,best=-1; while(l<=r){ mid=(l+r)>>1; if(solve(mid,n,k)){ best=mid; l=mid+1; } else r=mid-1; } printf("%d\n",best); } return 0; }
poj 3294 给你一些单词,求一个最长的子串,它在一半以上的单词中出现,可能会有多个
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 给你一些单词,求一个最长的子串 它在一半以上的单词中出现,可能会有多个答案 将所有的单词连接起来,中间都用前面未出现过,后面也不会出现的字符隔开 先二分答案的长度,转换成存在性问题,然后将连续的height【】>=mid的后缀分到一组 判断这些共同的前缀出现在了几个单词中(利用sa【】),如果大于等于一半的单词,则满足条件 */ #include<cstdio> #include<cstring> #include<set> using namespace std; const int maxn = 400010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); return ; } void da(int n,int m=500){//要注意m的取值,m大于所有出现过的字符,这道题目在单词与单词中间插了很多新的字符 int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } int type[maxn]; set<int> ss; bool solve(int mid,int n,int half){ ss.clear(); int i,j,k; for(i=2;i<=n;i++){ if(height[i]>=mid){ ss.insert(type[sa[i]]); ss.insert(type[sa[i-1]]); if(ss.size()>=half) return true; } else ss.clear(); } return false; } void print(int mid,int n,int half){ ss.clear(); int i,j,k; for(i=2;i<=n;i++){ if(height[i]>=mid){ ss.insert(type[sa[i]]); ss.insert(type[sa[i-1]]); } else { if(ss.size()>=half){ for(j=0;j<mid;j++) printf("%c",r[sa[i-1]+j]); puts(""); } ss.clear(); } } if((int)ss.size()>=half){ for(i=0;i<mid;i++) printf("%c",r[sa[n]+i]); puts(""); } } char str[maxn]; int main(){ int n,k,i,j,t=0; while(scanf("%d",&n),n){ memset(type,0,sizeof(type)); if(t) puts("");t++; k=0;int l=1,h=0,idx=124; for(i=1;i<=n;i++){ scanf("%s",str); int len=strlen(str); if(len>h) h=len; for(j=0;j<len;j++) { r[k]=str[j]; type[k++]=i; } if(i<n) r[k++]=idx++;//插入新字符 } if(n==1) {puts(str); continue;} da(k+1); int mid,best=-1; while(l<=h){ mid=(l+h)>>1; if(solve(mid,k,n/2+1)){ best=mid; l=mid+1; } else h=mid-1; } if(best==-1) puts("?"); else print(best,k,n/2+1); } return 0; }
spoj 694 求一个字符串不同子串的个数,r[len]=0一定要赋值,不然wa
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 后缀数组模板 rank[0~n-1] : 值域: 1~n sa[1~n] ; 值域: 0~n-1 height[2~n]: suffix[i-1]与suffix[i]的最长公共前缀 suffix(i-1) 与 suffix(i)是排名相邻的两个后缀 height[1]无意义 */ #include<cstdio> #include<cstring> #include<set> using namespace std; const int maxn = 400010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); return ; } void da(int n,int m=500){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } char str[maxn]; void solve(int n){ int ans=0; for(int i=1;i<=n;i++) ans+=n-sa[i]-height[i]; printf("%d\n",ans); } int main() { int t; scanf("%d",&t); while(t--) { scanf("%s",str); int len=strlen(str); for(int i=0;i<len;i++) r[i]=str[i];r[len]=0; da(len+1); solve(len); } return 0; }
poj 1743 求单个字符串的不可重叠最长重复子串
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
/* 一个字符串的不可重叠最长重复字串 先二分重复子串的长度,判断是否存在两个长度为mid的子串是相同的,且不重叠 利用height[]将排序后的后缀分成若干组,每组的后缀之间的height值都不小于mid 这样的话只要有一组的后缀满足sa的最大值与最小值之差>=k,就表示存在 注意m要够大!!! */ #include<cstdio> #include<cstring> const int maxn = 200010; int rank[maxn],sa[maxn],height[maxn]; int wa[maxn],wb[maxn],wv[maxn],buc[maxn]; int r[maxn+10]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } void calheight(int n){ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); return ; } void da(int n,int m=200){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[x[i]=r[i]]++; for(i=1;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ 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<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) buc[i]=0; for(i=0;i<n;i++) buc[wv[i]]++; for(i=0;i<m;i++) buc[i]+=buc[i-1]; for(i=n-1;i>=0;i--) sa[--buc[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } calheight(n-1); return ; } bool solve(int x,int n) { int i,max=-1,min=n; for(i=2;i<=n;i++) { if(height[i]>=x) { if(sa[i]>max) max=sa[i]; if(sa[i]<min) min=sa[i]; if(sa[i-1]>max) max=sa[i-1]; if(sa[i-1]<min) min=sa[i-1]; } else { max=-1,min=n+1; } if(max-min>x) return true; } return false; } int main() { int i,j,n,a,b; while(scanf("%d",&n),n) { scanf("%d",&b); for(i=0;i<n-1;i++) { scanf("%d",&a); r[i]=a-b+88; b=a; } da(n); int l=4,r=n/2+1,mid,best=-1; while(l<=r) { mid=(l+r)>>1; if(solve(mid,n)) { best=mid; l=mid+1; } else r=mid-1; } printf("%d\n",best+1); } return 0; }