P3809 【模板】后缀排序

【模板】后缀排序

题目背景

这是一道模板题。

题目描述

读入一个长度为 $ n $ 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序(用 ASCII 数值比较)从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 $ 1 $ 到 $ n $。

输入格式

一行一个长度为 $ n $ 的仅包含大小写英文字母或数字的字符串。

输出格式

一行,共 \(n\) 个整数,表示答案。

样例 #1

样例输入 #1

ababa

样例输出 #1

5 3 1 4 2

提示

\(1\le n \le 10^6\)

题目传送门

文字理解先参考注释
绝对不是我想咕

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char s[N];
int n,m;
int x[N],y[N],c[N],sa[N];
void get_SA()
{
	for(int i=1;i<=n;i++){++c[x[i]=s[i]];}//x:第一关键字 
	for(int i=2;i<=m;i++){c[i]+=c[i-1];}//c:一个桶,用来统计在x[i]这个关键字之前有多少个字符串,以此得出每一个字符串的排名 
	for(int i=n;i;i--){sa[c[x[i]]--]=i;}//将对应的排名映射到答案中 
	
	for(int k=1;k<=n;k<<=1)
	{
		int cnt=0;
		for(int i=n-k+1;i<=n;i++)
		{																					//第i种第一个关键字		//可理解为一个编号 
			y[++cnt]=i;//y:第二关键字,注意:此处的y并非第二关键字的值,而是一个指针,它表示在第一关键字编号为i的字符串在第二关键字中排在cnt的位置(实在不行参考后面统计答案时都写法) 
					   //对于第二关键字被映射到[n-k+1,n]的节点,他本身是没有第二关键字的,排在队头 (或者说是表示第二关键字为排名为cnt的字符串的第一关键字的标号是i)
		}
		for(int i=1;i<=n;i++)
		{
			if(sa[i]>k)//这个点在后缀数组中的位置(即第一关键字排序中的位置比k大) 
			{
				y[++cnt]=sa[i]-k;//将这一个点映射到的点(不是本身),排入y数组中 
			}
		}//所以后面枚举i代表了其在第二关键字中的排名 
		for(int i=1;i<=m;i++){c[i]=0;}
		for(int i=1;i<=n;i++){++c[x[i]];}
		for(int i=2;i<=m;i++){c[i]+=c[i-1];}
		//初始化并重新统计第一关键字的桶 
		for(int i=n;i;i--)
		{
			//y[i]:当前要确定排名的字符串的编号
			//i:他在第二关键字中的排名 
			sa[c[x[y[i]]]--]=y[i];y[i]=0;
			//从后往前:第一关键字相同的字符串,第二关键字靠后的排名靠后
			//(c[]数组一直自减,后遍历到的点说明其排名靠前) 
		}
		swap(x,y);//交换x,y当前的x将作为下一次的y参与排序 
		x[sa[1]]=1;cnt=1;
		for(int i=2;i<=n;i++)
		{
			//重新生成下一次排序的x 
			cnt+=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])^1;//如果当前节点和其对应的映射的第一关键字(的值,并非排名)完全相同,那么他们在下一次排序中的新第一关键字也相同 
        	x[sa[i]]=cnt;
		}
		if(n==cnt)break;
		m=cnt;
	}
}
void work()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	m=122;
	get_SA();
	for(int i=1;i<=n;i++)
	{
		printf("%d ",sa[i]);
	}
}
int main()
{
	freopen("P3809.in","r",stdin);freopen("P3809.out","w",stdout);
	work();
}
posted @ 2024-12-06 11:42  liuboom  阅读(8)  评论(0)    收藏  举报