后缀定义

“后缀i”代表以第i个字符开头的后缀,存储是用i代表字符串s的后缀s[i...n]

后缀数组是什么?

后缀数组(Suffix Array)主要关系到两个数组:sa 和 rk。

其中,sa[i] 表示将所有后缀排序后第 i 小的后缀的编号,也是所说的后缀数组,后文也称编号数组 sa;

rk[i] 表示后缀 i 的排名,是重要的辅助数组,后文也称排名数组 rk。

这两个数组满足性质:sa[rk[i]]=rk[sa[i]]=i。

解释

后缀数组示例:

后缀数组怎么求?

O(n^2logn) 做法

我相信这个做法大家还是能自己想到的:将盛有全部后缀字符串的数组进行 sort 排序,由于排序进行 O(n\log n) 次字符串比较,每次字符串比较要 O(n) 次字符比较,所以这个排序是 O(n^2\log n) 的时间复杂度。

O(nlog^2n) 做法

这个做法要用到倍增的思想。

首先对字符串 s 的所有长度为 1 的子串,即每个字符进行排序,得到排序后的编号数组 sa_1 和排名数组 rk_1。

倍增过程:

用两个长度为 1 的子串的排名,即 \(rk_1[i]\) 和 \(rk_1[i+1]\),作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 2 的子串:\(\{s[i\dots \min(i+1, n)]\ |\ i \in [1,\ n]\}\) 进行排序,得到 sa_2 和 rk_2;

之后用两个长度为 2 的子串的排名,即 rk_2[i] 和 rk_2[i+2],作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 4 的子串:\(\{s[i\dots \min(i+3, n)]\ |\ i \in [1,\ n]\}\) 进行排序,得到 sa_4 和 rk_4;

以此倍增,用长度为 w/2 的子串的排名,即 \(rk_{w/2}[i]\) 和 \(rk_{w/2}[i+w/2]\),作为排序的第一第二关键字,就可以对字符串 s 的每个长度为 w 的子串 \(s[i\dots \min(i+w-1,\ n)]\) 进行排序,得到 sa_w 和 rk_w。其中,类似字母序排序规则,当 i+w>n 时,\(rk_w[i+w]\) 视为无穷小;

\(rk_w[i]\) 即是子串 \(s[i\dots i + w - 1]\) 的排名,这样当 w \geqslant n 时,得到的编号数组 sa_w,也就是我们需要的后缀数组。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
char s[N];
int n, w, sa[N], rk[N << 1], oldrk[N << 1];
// 为了防止访问 rk[i+w] 导致数组越界,开两倍数组。
// 当然也可以在访问前判断是否越界,但直接开两倍数组方便一些。
int main() {
int i, p;
scanf("%s", s + 1);
n = strlen(s + 1);
for (i = 1; i <= n; ++i)
sa[i] = i, rk[i] = s[i];
for (w = 1; w < n; w <<= 1) {
sort(sa + 1, sa + n + 1, [](int x, int y) {
return rk[x] == rk[y] ? rk[x + w] < rk[y + w] : rk[x] < rk[y];
}); // 这里用到了 lambda
memcpy(oldrk, rk, sizeof(rk));
// 由于计算 rk 的时候原来的 rk 会被覆盖,要先复制一份
for (p = 0, i = 1; i <= n; ++i) {
if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&
oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
rk[sa[i]] = p;
} else {
rk[sa[i]] = ++p;
} // 若两个子串相同,它们对应的 rk 也需要相同,所以要去重
}
}
for (i = 1; i <= n; ++i)
printf("%d ", sa[i]);
return 0;
}

后缀数组C++详解的更多相关文章

  1. js数组方法详解

    Array对象的方法-25个 /*js数组方法详解 */ /* * 1 concat() 用于连接多个数组或者值-------------- * 2 copyWithin() 方法用于从数组的指定位置 ...

  2. JavaScript数组方法详解

    JavaScript数组方法详解 JavaScript中数组的方法种类众多,在ES3-ES7不同版本时期都有新方法:并且数组的方法还有原型方法和从object继承的方法,这里我们只介绍数组在每个版本中 ...

  3. 最新java数组的详解

    java中HashMap详解 http://alex09.iteye.com/blog/539545 总结: 1.就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java ...

  4. JS数组映射详解

    现在这里占个坑位,免的忘了,需要整理一下最近的内容: 1.数组映射的使用 2.微信分享功能详解 3.jq自己封装 4.HTML的富文本应用

  5. [转载]Splay Tree数组实现+详解

    变量声明:f[i]表示i的父结点,ch[i][0]表示i的左儿子,ch[i][1]表示i的右儿子,key[i]表示i的关键字(即结点i代表的那个数字),cnt[i]表示i结点的关键字出现的次数(相当于 ...

  6. JavaScript Array数组方法详解

    Array类型是ECMAScript中最常用的引用类型.ECMAScript中的数据与其它大多数语言中的数组有着相当大的区别.虽然ECMAScript中的数据与其它语言中的数组一样都是数据的有序列表, ...

  7. [转]Java数组初始化详解

    一维数组1)   int[] a;   //声明,没有初始化 2)   int[] a=new int[5];   //初始化为默认值,int型为0 3)   int[] a={1,2,3,4,5}; ...

  8. javascript数组常用方法详解

    1,splice().   array.splice(index,many,list1,list2....)  参数1.index位置 负数为从结尾处算,倒数第一为-1:参数2,many要删除的项目, ...

  9. iOS 判断数组array中是否包含元素a,取出a在array中的下标+数组方法详解

    目前找到来4个解决办法,第三个尤为简单方便 NSArray * arr = @["]; //是否包含 "]) { NSInteger index = [arr indexOfObj ...

  10. js 类数组arguments详解

    arguments并不是一个真正的数组,而是一个"类似数组(array-like)"的对象: 就像下面的这段输出,就是典型的类数组对象: [, , callee: ƒ, Symbo ...

随机推荐

  1. 数据库定时备份linux篇

    目录 1 序言 2 crond 相关知识点 2.1 crond 是什么? 2.2 crontab 进行定时任务设置 2.2.1 crontab 指令选项说明 2.2.2 crontab 指令使用格式 ...

  2. Vue拖拽排序

    转载至https://www.crazyming.com/note/757/ 使用拖拽功能来实现排序. 需要先学习w3cschool 关于拖拽的教程:http://www.w3school.com.c ...

  3. 【Azure Redis 缓存】使用开源工具redis-copy时遇见6379端口无法连接到Redis服务器的问题

    问题描述 当使用Azure Redis服务时,需要把一个Redis服务的数据导入到另一个Redis上,因为Redis服务没有使用高级版,所以不支持直接导入/导出RDB文件. 以编程方式来读取数据并写入 ...

  4. Django4全栈进阶之路14 项目实战(用户管理):base.html基础模板设计

    在 Django 中,我们可以使用模板继承来避免代码的重复.模板继承是指我们可以在一个模板中定义一些公共的 HTML 代码,然后在其他模板中继承这个基础模板,并根据需要添加或覆盖一些内容. 通常情况下 ...

  5. Python encode()方法和decode()方法

    Python encode()方法 encode() 方法为字符串类型(str)提供的方法,用于将 str 类型转换成 bytes 类型,这个过程也称为"编码".encode() ...

  6. Rocky 9 Linux 平台 vim 9.0 源码包编译安装踩坑记录

    目录 vim 9.0 部署准备环境 vim 9.0 源码包正式部署 vim 9.0 初体验 plug-vim 安装插件 在上一篇 <vim入门实战> 篇,我并没有介绍 Linux 平台源码 ...

  7. 学习原根 by OI-wiki

    根据 OI-wiki 的讲解,加以自己的理解和简化.偏重于算法竞赛而不是数学竞赛. 前置知识: 费马小定理:\(a^{p-1} \equiv 1(\mod p)\),\(p\) 为质数. 欧拉定理:\ ...

  8. Flutter状态管理新的实践

    1 背景介绍 1.1 声明式ui 声明式UI其实并不是近几年的新技术,但是近几年声明式UI框架非常的火热.单说移动端,跨平台方案有:RN.Flutter.iOS原生有:SwiftUI.android原 ...

  9. SpringBoot之MVC配置(WebMvcConfigurer详解)

    一:基本介绍 Spring MVC是一种常用的Java Web框架,它提供了一种基于MVC模式的开发方式,可以方便地实现Web应用程序.在Spring MVC中,WebMvcConfigurer是一种 ...

  10. 使用libavcodec将mp3音频文件解码为pcm音频采样数据【[mp3float @ 0x561c1ec49940] Header missing】

    一.打开和关闭输入文件和输出文件 想要解决上面提到的问题,我们需要对mp3文件的格式有个大致了解,为了方便讲解,我这里画了个示意图: ID3V2 包含了作者,作曲,专辑等信息,长度不固定,扩展了 ID ...