后缀数组倍增预处理
#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define N 2200000
#define L 2000000
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
char ch=0;
int x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*flag;
}
char s[N];
int n,m;
int c[N],x[N],y[N],sa[N];
int main()
{
scanf("%s",s+1);
n=strlen(s+1);m=122;
for(int i=1;i<=n;i++)++c[x[i]=s[i]];
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
//sa[i]当前是按照第一关键字排序的第i名的后缀编号
//y[i]是第二关键字的排名为i的数,它第一关键字的位置,根据定义,下面是sa[i]-k
int num=0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]]++;//按照第一关键字扔到桶里。
for(int i=1;i<=m;i++)c[i]+=c[i-1];//求一下前缀。
for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
//按照第二关键字的顺序倒着取
//先看一下这个第二关键字对应的第一关键字的位置
//用x来确定一下排名它当前的排名
//用桶来算出它在下一次会排在第几名
//根据sa数组的定义,在下一轮中,sa[这个排名]=第一关键字的位置
swap(x,y);//只是为了避免多开一个数组,利用y来做一个x的拷贝而已
x[sa[1]]=num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?num:++num;
//这里的y数组是之间的x数组
//分别比较这一个和上一个的两个关键字是否同时相同。
if(num==n)break;
m=num;
}
for(int i=1;i<=n;i++)printf("%d ",sa[i]);
return 0;
}
处理height数组
height[i]=lcp(sa[i],sa[i-1])
为了得到height数组,可以先处理出一个h数组
h[i]=height[rank[i]]
由此可以推出height[i]=h[sa[i]]
考虑暴力计算这个h[i]数组是O(n^2)的
但这里有一个很重要的性质,即h[i]>=h[i-1]-1
手动模拟一下即可证明。
因此,这个东西可以O(n)来求。
void calheight()
{
for(int i=1,k=0;i<=n;i++)
{
if(k)k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
其中这个k就代表了h[i]的意思