题目:后缀排序

什么是后缀数组?他主要包含两个数组:sa和rk。

其中sa[i]表示将字符串后缀排序后第i小的编号,rk[i]表示后缀i的排名。

显然sa[rk[i]]=i,rk[sa[i]]=i。

例如字符串aba,他的后缀aba,ba,a,排序后a,aab,ab,此时

| i | 1 | 2 | 3 |

| sa | 3 | 1 | 2 |

| rk | 2 | 3 | 1 |

观察上面给出的式子,发现都成立。

那如何求后缀数组呢,最简单的方法是直接排序,不要以为时间复杂度是O(nlogn),字符串比较还有一层O(n),所以总时间复杂度是O(n^2logn)的,显然不行。

既然快排不行,那用别的排序咯,我们发现字符个数很少,因此采用基数排序,但是朴素的基数排序是O(n^2)更慢了,因此这里使用倍增优化,每次枚举范围扩大一倍,为什么可行?

试想一下,如果对于基数排序,我们也可以通过压位,以10,100,甚至更多为一个单位,显然答案不会改变,只是效率的改变罢了,因此这里同理可以得到。

具体讲一下做法,设字符串s,先求出所有s[i]的排名,这就是最基础的对应关系,也就是以一个字符为基准的方法个数,接下来就是枚举长度了,每次求出二元组(k1[i],k2[i])表示排序的两个关键字,以k2[i]为关键字排序,然后再以k1[i]为关键字排,排序号后,求出所有数的排名,如果最大的编号为字符串长度,那么就说明已经完成了后缀排序,此时sa和rk中存储的就是最开始所说的内容。

时间复杂度O(nlogn),一个比较优秀的算法。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const int N=1e6+5;
using namespace std;
int n,cnt[N],rk[N],psa[N],sa[N],k1[N],k2[N],m;
char s[N];
int main()
{
scanf("%s",s);
n=strlen(s);
m=max(n,300);
for(int i=0;i<n;i++)
cnt[(int)s[i]]++;
for(int i=1;i<m;i++)
cnt[i]+=cnt[i-1];
for(int i=0;i<n;i++)
rk[i]=cnt[(int)s[i]]-1;
for(int w=1;w<n;w<<=1)
{
for(int i=0;i<n;i++)
{
if(i+w>=n)
k2[i]=0;
else
k2[i]=rk[i+w];
k1[i]=rk[i];
}
for(int i=0;i<n;i++)
cnt[i]=0;
for(int i=0;i<n;i++)
cnt[k2[i]]++;
for(int i=1;i<m;i++)
cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;i--)
psa[--cnt[k2[i]]]=i;
for(int i=0;i<n;i++)
cnt[i]=0;
for(int i=0;i<n;i++)
cnt[k1[i]]++;
for(int i=1;i<m;i++)
cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;i--)
sa[--cnt[k1[psa[i]]]]=psa[i];
int tmp=1;
rk[sa[0]]=1;
for(int i=1;i<n;i++)
if(k1[sa[i]]==k1[sa[i-1]] && k2[sa[i]]==k2[sa[i-1]])
rk[sa[i]]=tmp;
else
rk[sa[i]]=++tmp;
if(tmp==n)
break;
}
for(int i=0;i<n;i++)
printf("%d ",sa[i]+1);
return 0;
}

最新文章

  1. 浏览器访问Servlet
  2. session的工作原理
  3. MVC后台数据赋值给前端JS对象
  4. Linux下搭建PHP环境
  5. C# FTP上传
  6. jquery,js引入css文件,js引入头尾
  7. Python3 学习第十弹: 模块学习三之数字处理
  8. MySQL之连接数据库的两种方法
  9. cocos2d-x 多分辨率适配详解(转载),以前北京团队设计的游戏,也是用这套方案
  10. js【输入一个日期】返回【当前12个月每月最后一天】
  11. 查看 yum 安装软件包的路径
  12. asp.net的ajax以及json
  13. HDU 5352 MZL&#39;s City
  14. Spark技术内幕之任务调度:从SparkContext开始
  15. android 优化之布局优化
  16. 初步了解PE分析
  17. unity5.6 导出gradle工程,Android Studio 导入问题以及解决
  18. python base64 decode incorrect padding错误解决方法
  19. 什么是TF-A?
  20. RabbitMq简单应用

热门文章

  1. 【Traefik二次开发】中间件 Middleware 开发
  2. Windows Server体验之管理
  3. 《Java基础——构造器(构造方法)》
  4. Python数据科学手册-Pandas:层级索引
  5. linux系统排查数据包常用命令
  6. PostgreSQL 创建表格
  7. C#并发编程-1 并发编程概述
  8. PHP全栈开发(四): HTML 学习(3. form 表单)
  9. pycharm下载与使用
  10. 撸了一个简易的配置中心,顺带整合到了SpringCloud