P3809 【模板】后缀排序

【模板】后缀排序

题目背景

这是一道模板题。

题目描述

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

输入格式

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

输出格式

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

样例 #1

样例输入 #1

ababa

样例输出 #1

5 3 1 4 2

提示

1n106

题目传送门

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

#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 @   liuboom  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示