2022春每日一题:Day 13

题目:后缀排序
什么是后缀数组?他主要包含两个数组: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;
}
2022春每日一题:Day 13的更多相关文章
- <每日一题>题目13:列表的简单问题
''' 分析: python赋值是通过指针来进行的. 很显然第一.三.四次调用都指向同一个列表,并未完成清空, 第二次调用只是指向了另一个列表,也未完成清空,很显然结果是累计的 结果: [0, 1] ...
- CISP/CISA 每日一题 13
监控信息系统人员所提供服务的效率和效果的工具: 1.例外报告:识别所有没有成功完成的或出了故障的应用 2.作业重运行报告:大多数异常终止作业都会导致重起 3.操作员问题报告:记录计算机运行问题及解决方 ...
- 老男孩IT教育-每日一题汇总
老男孩IT教育-每日一题汇总 第几天 第几周 日期 快速访问链接 第123天 第二十五周 2017年8月25日 出现Swap file….already exists以下错误如何解决? 第122天 2 ...
- PL/SQL Challenge 每日一题:2014-3-14 11gR2中带RELIES_ON子句的RESULT_CACHE函数
PL/SQL Challenge 每日一题:2014-3-14 11gR2中带RELIES_ON子句的RESULT_CACHE函数 最先答对且答案未经编辑的puber将获得纪念章一枚(答案不可编辑但可 ...
- CISP/CISA 每日一题 五
CISA 每日一题(答) 信息系统审计师要确认系统变更程序中的: 1.变更需求应有授权.优先排序及跟踪机制: 2.日常工作手册中,明确指出紧急变更程序: 3.变更控制程序应同时为用户及项目开发组认可: ...
- 「每日一题」有人上次在dy面试,面试官问我:vue数据绑定的实现原理。你说我该如何回答?
关注「松宝写代码」,精选好文,每日一题 时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 来源:原创 一.前言 文章首发在「松宝写代码」 2020. ...
- [每日一题]面试官问:谈谈你对ES6的proxy的理解?
[每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...
- 【JavaScript】Leetcode每日一题-二叉搜索树的范围和
[JavaScript]Leetcode每日一题-二叉搜索树的范围和 [题目描述] 给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和. 示例1: 输入: ...
- 【python】Leetcode每日一题-寻找旋转排序数组中的最小元素
[python]Leetcode每日一题-寻找旋转排序数组中的最小元素 [题目描述] 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组nums ...
- 【JavaScript】【dp】Leetcode每日一题-解码方法
[JavaScript]Leetcode每日一题-解码方法 [题目描述] 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 'A' -> 1 'B' -> 2 ... 'Z' -& ...
随机推荐
- 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(18)-Fiddler如何接口测试,妈妈再也不担心我不会接口测试了
1.简介 Fiddler最大的优势在于抓包,我们大部分使用的功能也在抓包的功能上,fiddler做接口测试也是非常方便的. 领导或者开发给你安排接口测试的工作任务,但是没有给你接口文档(由于开发周期没 ...
- Typora破解下载
Typora破解下载 文章指路
- Ingress资源规范
k8s v1.19版本中Ingress资源规范从v1beta1版本升级至稳定的v1版本 v1beta1版本 v1beta1版本的Ingress资源位于API群组的extensions之中,该版本的资源 ...
- Kafka QuickStart
环境版本 操作系统:CentOS release 6.6 (Final) java版本: jdk1.8 kafka 版本: kafka_2.11-1.1.1.tgz 安装kafka 1. 下载压缩包, ...
- 2022IDEA破解
注意 本教程适用于 IntelliJ IDEA 2022.1.2 以下所有版本,请放心食用~ 本教程适用于 JetBrains 全系列产品,包括 IDEA.Pycharm.WebStorm.Phpst ...
- 《3-D Deep Learning Approach for Remote Sensing Image Classification》论文笔记
论文题目<3-D Deep Learning Approach for Remote Sensing Image Classification> 论文作者:Amina Ben Hamida ...
- 不安装运行时运行 .NET 程序 - NativeAOT
大家好,先祝大家国庆快乐.不过大家看到这篇文章的时候估计已经过完国庆了 . 上一篇我们写了如何通过 SelfContained 模式发布程序(不安装运行时运行.NET程序)达到不需要在目标机器上安装 ...
- 220722 T4 求和 /P4587 [FJOI2016]神秘数 (主席树)
好久没打主席树了,都忘了怎么用了...... 假设我们选了一些数能构成[0,x]范围内的所有值,下一个要加的数是k(k<=x+1),那么可以取到[0,x+k]内的所有取值,所以有一种做法: 对于 ...
- 达梦dba_segments指定表名查询到的大小都包含哪些数据
一.结论 dba_segments指定表名查询到的段大小包含索引.约束.表字段数据(包含LOB字段)(1)表(不包含LOB字段)创建默认分配2个簇,1个簇用于存放表结构及字段数据,1个簇用于存放clu ...
- 使用LEFT JOIN 统计左右存在的数据
最近做了一个数据模块的统计,统计企业收款.发票相关的数据,开始统计是比较简单,后面再拆分账套统计就有点小复杂,本文做一个简单的记录. 需求 企业表 企业表t_company有如下字段:标识id.企业名 ...