P3809 【模板】后缀排序
第一次写,第一次写,第一次写
重要的事情说三遍,记录一下,注释代码便于复习。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+5;
char s[N];
int n,sz,sa[N],rk[N],tp[N],b[N];
//s[i] 字符串
//n 字符串的长度
//sz桶的大小
//sa[i] 排名为第i的后缀下标
//rk[i] 下标为i的后缀的排名
//tp[i] 按第二关键字为i的后缀的下标
//b[i] 排名为第i的桶所装的名次个数.
void Qsort(){ //基数排序.
for(int i=0;i<=sz;i++) b[i]=0;//桶清空.
for(int i=1;i<=n;i++) b[rk[i]]++;//记录排名次数
for(int i=1;i<=sz;i++) b[i]+=b[i-1];//求前缀和
for(int i=n;i>=1;i--) sa[b[rk[tp[i]]]--]=tp[i];//更新后缀数组sa
//当第一关键字相同,按照第二关键字从小到大排名.
//这里i倒序 是求的是当第一关键字相同时 第二关键字较大的排名的下标
}
void SA(){
sz=80;//初始化桶. 只包含字母和数字,'0'-'z' 80个够用了.
for(int i=1;i<=n;i++) rk[i]=s[i]-'0'+1,tp[i]=i;//初始化排名和第二关键字的排名.
Qsort();//更新后缀数组.
for(int w=1,id=0;id<n;sz=id,w<<=1){ //w 当前已经得到的倍增长度 id当前排名个数.
id=0;
for(int i=n-w+1;i<=n;i++) tp[++id]=i;
for(int i=1;i<=n;i++) if(sa[i]>w) tp[++id]=sa[i]-w;
Qsort(); //用已经求得的2w后缀对应的 tp排名 来更新下一轮的sa.
swap(rk,tp);//这一轮的tp已经没用,tp初始化为rk 用来更新下一轮的rk.
rk[sa[1]]=id=1;//更新下一轮的rank.
for(int i=2;i<=n;i++) //如果本轮两个人排名和第二关键字排名相同,说明下一轮排名是相同的,否则排名++.
rk[sa[i]]= (tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?id:++id;
}
for(int i=1;i<=n;i++)
printf("%d ",sa[i]);
puts("");
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
SA();
}