用倍增思想求(sa_{k}[],rk_{k}[] -> sa_{2k}[],rk_{2k}[])
用基数排序,排序二元组((rk[i],rk[i+k]))
复杂度(O(Nlog{N}))
#include<bits/stdc++.h>
const int N=1000010;
char s[N];
int n,m,num;
int sa[N],rk[N],bac[N],y[N],tmp[N];//sa[i]数组表示后缀排名为i的位置,rk[i]表示后缀[i..n]的排名
int height[N];//height[i]表示rk为i的后缀与rk为i-1的后缀的LCP
void getsa(){
//rk[i]表示位置i的第一关键字(排名)
//二元组(rk[i],rk[i+k])
//初始化基数排序(单字符,其实是单元)
for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++;//bac[i]表示第一关键字小于i的个数
for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐个统计,再求前缀和,相当于用桶计数
for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i;//在第二关键字有序的情况下,对位置按照第一关键字的bac数组,逐个附排名
for(int k=1;k<=n;k<<=1){
num=0;//排名的数量,初始化为单个字符的ASCLL码上限,m=ascll('z')=122
//y[i]表示第二关键字排名为i的二元组的第一关键字位置(i)
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;//利用已有的sa_(k/2)数组,rk靠前的且有第一关键字的,将第一关键字位置sa_(k/2)[i]-k放入y
//在上一轮中,rk_k数组已经求好了,倍增求解,
//接下来利用基数排序,求出sa_k
for(int i=1;i<=m;i++)bac[i]=0;
for(int i=1;i<=n;i++)bac[rk[i]]++;//bac[i]表示第一关键字小于i的个数
for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐个统计,再求前缀和,相当于用桶计数
for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i];//在第二关键字有序的情况下,对位置按照第一关键字的bac数组,逐个附排名
memcpy(tmp,rk,sizeof(tmp));//用tmp存一下rk_(k/2),在求rk_k时仍会用到
//其实就是利用sa_k和rk_(k/2)数组求rk_k
rk[sa[1]]=1;num=1;
for(int i=2;i<=n;i++){
if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num;
else rk[sa[i]]=++num;
}
if(num==n)break;//已经有了n种排名
m=num;//m表示排名的数量(在桶排中为值域)
}
}
void geth(){
//rk[i]=rk1,sa[rk1-1]=x;
//rk[i-1]=rk2,as[rk2-1]=y;
//height[rk1]=lcp(s[i..],s[x...]);
//height[rk2]=lcp[s[i-1..],s[y...]);
//lcp(s[i..],s[x..])>=lcp(s[i-1...],s[y...])
for(int i=1;i<=n;i++){
int x=sa[rk[i]-1];
int k=std::max(0,height[rk[i-1]]-1);
while(s[i+k]==s[x+k])++k;
height[rk[i]]=k;
}
}
int main(){
scanf("%s",s+1);n=strlen(s+1);m=122;
getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]);
printf("
");
geth();for(int i=2;i<=n;i++)printf("%d ",height[i]);
return 0;
}
无注释版:
#include<bits/stdc++.h>
const int N=1000010;
char s[N];
int n,m,num;
int sa[N],rk[N],bac[N],y[N],tmp[N];
void getsa(){
for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++;
for(int i=2;i<=m;i++)bac[i]+=bac[i-1];
for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i;
for(int k=1;k<=n;k<<=1){
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++)bac[i]=0;
for(int i=1;i<=n;i++)bac[rk[i]]++;
for(int i=2;i<=m;i++)bac[i]+=bac[i-1];
for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i];
memcpy(tmp,rk,sizeof(tmp));
rk[sa[1]]=1;num=1;
for(int i=2;i<=n;i++){
if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num;
else rk[sa[i]]=++num;
}
if(num==n)break;
m=num;
}
}
int main(){
scanf("%s",s+1);n=strlen(s+1);m=122;
getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]);
return 0;
}