先上da算法(n*logn)求height的板子:
注意:height的有效值是2-n,sa的有效值是1-n,rank的有效值是0-n-1.
str 转成 r的时候,是从第0位到n位,最后的\0也带上,不然会RE

const int MAXN=2e3+5;
int t1[MAXN], t2[MAXN], c[MAXN];
bool cmp(int *r, int a, int b, int l){
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(int str[], int sa[], int rk[], int height[], int n, int m)
{
n++;
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]=str[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;
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];
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;
}
int k=0;
n--;
for(i=0; i<=n; i++) rk[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k) k--;
j=sa[rk[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rk[i]]=k;
}
}
int rk[MAXN], sa[MAXN], height[MAXN], r[MAXN];
char str[MAXN];
例题:求不相同子串的个数(SPOJ 694)
题解:不同字串等于全部的子串-相同的子串个数(height的和)

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e3+5;
int t1[MAXN], t2[MAXN], c[MAXN];
bool cmp(int *r, int a, int b, int l){
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(int str[], int sa[], int rk[], int height[], int n, int m)
{
n++;
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]=str[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;
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];
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;
}
int k=0;
n--;
for(i=0; i<=n; i++) rk[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k) k--;
j=sa[rk[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rk[i]]=k;
}
}
int rk[MAXN], sa[MAXN], height[MAXN], r[MAXN];
char str[MAXN];
int main()
{
int T;
for(cin>>T; T--; )
{
scanf("%s", str);
int n=strlen(str);
for(int i=0; i<=n; i++)
r[i]=str[i];
da(r, sa, rk, height, n, 128);
int ans=n*(n+1)/2;
for(int i=2; i<=n; i++)
ans-=height[i];
cout<<ans<<endl;
}
return 0;
}
例题:不可重叠最长重复子串(POJ 1743)
题解:二分答案k(子串的长度),转化成判定性问题,在扫一遍height,判断是否存在一段的height(该段的height都大于等于k)中的重复子串不相交。、
本题题意:n个数组成的串,求是否有多个满足条件且不重叠的子串的长度大于等于5,有的话输出子串长度(最长)否则0,条件:子串的每一位和前一位的数字差相等。
记录原串中每一位和前一位的差,在这个串中长度为n的串即对应原串中长度为n+1的串,注意这里还要满足子串要有间隔,否则对应的原串是恰好相连的。
细节:sa算法中涉及到了桶排序,数字不可为负,为此可以同时加上一个数。

// 全部用scanf 220ms
// 用解除流绑定的cin 720ms
// 不太明白为什么时间差了这么多
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=2e4+5;
int t1[MAXN], t2[MAXN], c[MAXN];
int rk[MAXN], sa[MAXN], height[MAXN], r[MAXN];
int a[MAXN];
bool cmp(int *r, int a, int b, int l){
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(int str[], int sa[], int rk[], int height[], int n, int m)
{
n++;
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]=str[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;
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];
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;
}
int k=0;
n--;
for(i=0; i<=n; i++) rk[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k) k--;
j=sa[rk[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rk[i]]=k;
}
}
bool check(int k, int n)
{
int L=sa[1], R=sa[1];
for(int i=2; i<=n; i++)
{
if(height[i]>=k)
{
L=min(L, sa[i]);
R=max(R, sa[i]);
if(R-L>k) //这里是大于,要有间隔
return true;
}
else
L=R=sa[i];
}
return false;
}
int main()
{
//ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n;
while(cin>>n, n)
{
//for(int i=0; i<n; i++) cin>>a[i];
for(int i=0; i<n; i++) scanf("%d", &a[i]);
n--;
for(int i=0; i<n; i++) r[i]=a[i+1]-a[i]+100;
r[n]=0;
da(r, sa, rk, height, n, 256);
int l=1, r=n/2; //这种二分从1开始
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid, n)) l=mid+1;
else r=mid-1;
}
if(r>=4) cout<<r+1<<endl;
else cout<<"0"<<endl;
}
return 0;
}
