题目
题目描述
读入一个长度为 n 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 1 到 n。
输入格式
一行一个长度为 n 的仅包含大小写英文字母或数字的字符串。
输出格式
一行,共 n 个整数,表示答案。
输入输出样例
输入
ababa
输出
5 3 1 4 2
题意
把某个字符串的所有后缀都列举出来,然后根据字典序排序
样例过程如下
思路
以下内容摘自大佬的后缀排序博客
基数排序
首先要了解基数排序
基数排序是一种给数对排序的方法
有第一关键字和第二关键字,就和pair一样
基数排序的流程为
1、初始化桶a(a[i]的作用是记录第一关键字中排位第i的数的位置)
如第一关键字序列为1 3 2 1 4 3 1 2
则a数组分别为 3 2 2 1
代表第一关键字序列中1元素有3个,2元素有2个,以此类推
第一关键字的数据范围为<=m
for(int i=1;i<=m;i++)a[i]=0;
2、把第一关键字rk数组存入桶中
for(int i=1;i<=n;i++)++a[rk[p]];
如rk数组为 1 3 2 1 4 3 1 2
则a数组分别为 3 2 2 1
3、做a数组的前缀和
for(int i=1;i<=m;i++)a[i]+=a[i-1];
做前缀和的意义在于,能得到rk数组每个元素最大的排位数
如上面的例子,a数组做前缀和后变成
3 5 7 1
代表
元素1占据了1~3位
元素2占据了4~5位
元素3占据了6~7位
元素4占据了8~8位
一定程度上,第一关键字已经排好序了
最复杂且难以理解的最后一步
4、一个逆序枚举
for(int i=n;i>=1;i--)Sa[a[rk[tp[i]]]--]=tp[i];
一看到这个,好像很复杂
实际上一层一层剥开还是可以理解的
首先理解tp数组
tp[i]:排名为i的第二关键字的位置
比如第二关键字序列为
3 2 1 2 3 3 1 3
则tp数组为
3 7 2 4 1 5 6 8
代表排名为1的第二关键字位置为3,排名为2的第二关键字位置为7,以此类推。
rk[tp[i]]:刚刚知道,rk数组存储了第一关键字,那么这两个数组组合起来什么意思呢?
其实就是排名为i的第二关键字对应的第一关键字
我们再套一层
a[rk[tp[i]]]:排名为i的第二关键字对应的第一关键字的最大排位数
最后
Sa[a[rk[tp[i]]]–]=tp[i]:
首先明白Sa[i]表示排名第i的数对的位置
现在我们先解释为啥要逆序枚举i
因为我们要从第二关键字的最低排位入手去推断这个数对的排位
啥是最低排位,就是数字最大的排位。
i从后往前枚举因为排名最后的第二关键字必然会使得这个数对的排位变成相同第一关键字条件下的最后一位
比如比较数对……(2,3)、(2,1)……
这两个数对第一关键字相同,已知第一关键字2的排位为4~5,第二关键字排位已知3是第8位,即最低排位,所以(2,3)的排位必然是5,(2,1)的排位必然是4
a[rk[tp[i]]]需要减减的原因是不能重复使用排位,以上面为例子
(2,3)占据了第5位,(2,1)就不能是第5位,只能是第4位
后面的操作明天再补