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();
}