以下代码来自 Leo_h ,加了一点注释,稍微改了一点地方。
//from leo_h
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 100005
#define rank RANK
int n;
char str[maxn];
int cnt[maxn];
int t1[maxn],t2[maxn];
int sa[maxn]; //sa[i]表示排名为i的后缀
int rank[maxn];//rank[i]表示后缀i的排名
int height[maxn];
void build()
{
int *x=t1,*y=t2;//x,y为两个临时数组
int m=26;//下一次编号的总个数
//按照第一个字符的大小把后缀安排到后缀数组中 :
for(int i=0;i<n;i++) cnt[x[i]=(str[i]-'a')]++;
//统计每种字母出现的次数
for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
//为每种字母出现次数做前缀和
for(int i=n-1;i>=0;i--) sa[--cnt[x[i]]]=i;
//这样的做法保证了相同的字母被放到了连续的一块
//原理类似于计数排序(只有一位的基数排序)
for(int k=1;k<=n;k<<=1)//考虑每一个后缀的前k位
{
//x为上一次的编号,y为这一次的序列
int p=0;//当前序列长度
//利用sa排序第二关键字
for(int i=n-1;i>=n-k;i--) y[p++]=i;
//后k个后缀没有第二关键字(或称第二关键字为0),直接放到最前面就好了
for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
//y[i]:position of the suffix having the i-th "next k num",similar to sa
//把长度大于等于k的后缀,按照sa[i]中的原顺序放到y里
//现在我们按照第一关键字再排序
memset(cnt,0,sizeof(cnt));
for(int i=0;i<p;i++) cnt[x[y[i]]]++;
//统计每一种编号出现的次数
for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
//为每种编号的出现次数计算前缀和
for(int i=p-1;i>=0;i--) sa[--cnt[x[y[i]]]]=y[i];
//give a higher rank to suffix with larger second keyword when having same first one
//按照第一关键字排序, 基数排序
swap(x,y);//this time,y stores the information
//y为上一次的编号,x为这一次的序列,我们打算把x直接覆盖掉
p=0;//统计不同的编号的出现次数,
//编号从0开始所以当前的最后一个编号为p-1
for(int i=0;i<n;i++)//计算下一次的编号,并存入x中
x[sa[i]]= (i==0||y[sa[i]]!=y[sa[i-1]]||y[sa[i]+k]!=y[sa[i-1]+k]) ? p++:p-1;
//考虑sa中两个相邻的位置的编号,
//要么相同,要么后者是前者+1
//不妨令 sa[-1] 的编号为 -1
//当: i == 0 或 上一次的两个权位中有任意一个不同 时,新开一个编号p
// sa[0] 的编号为 sa[-1]的编号+1
if(p>=n) break;//所有编号都不同,排序完成
m=p;//下一次的编号总个数为p
}
for(int i=0;i<n;i++) rank[sa[i]]=i;//rank 是 sa 的反函数
int k=0;
for(int i=0;i<n;i++)//构造height数组
{
if(k) k--;//重要的一条性质 height[rank[i]] >= height[rank[i-1]]-1
if(rank[i]==0) continue;//height[0]不存在
int j=sa[rank[i]-1];//后缀j 为 后缀i在sa中的 上一个后缀
while(str[i+k]==str[j+k]) k++;//向后检查直到不匹配为止
height[rank[i]]=k;//第一个不匹配位置的下标就是匹配的长度
}
}
int main()
{
//freopen(".in","r",stdin);
//scanf("%d",&n);
scanf("%s",str);
n=strlen(str);
build();
for(int i=0;i<n;i++) printf("%4d ",sa[i]);
putchar('\n');
for(int i=0;i<n;i++) printf("%4d ",rank[i]);
putchar('\n');
for(int i=0;i<n;i++) printf("%4d ",height[i]);
return 0;
}