「Note」后缀数组
上课直接听晕过去。。。
$\mathbb{Problem}$
给定一个字符串,要求对其所有的后缀进行排序。
朴素做法直接是按字典序写一个 cmp 函数,但是两两之间比较的复杂度会飙升到 $\Theta(n)$,sort 一遍下来是 $\Theta(n ^ 2 log_2^n)$ 的时间复杂度,显然不够优,此时可以引进 后缀数组 求解。
$\mathbb{Definitions}$
后缀数组 $\mathtt{Suffix Array}$ 由两个主要的数组组成。一个是 后缀编号 $\mathtt{sa}$ ,一个是 数组排名 $\mathtt{rnk}$。
其实是顾名思义, $\mathtt{sa_i}$ 表示 排名为 $i$ 的后缀的编号, $\mathtt{rnk_i}$ 表示 编号为 $i$ 的后缀的排名。
表示可能和课上略有所出入,最终目的是便于理解。
$\mathbb{Build}$
思想和 基数排序 类似。
如果不会基排,其实也无所谓。
既然提到 基数排序 和 上面的 朴素做法, 那么可以考虑对于 $\Theta(n ^ 2 log_2^n)$ 的算法进行优化。
这里直接给出做法。
当时上课时看到就傻了,这个倍增是个什么鬼哟? 其实 ppt 上有一句
利用上一轮比较的结果
也不难想到对于后缀 $[i, n]$ 的长度为 $len$ 的前缀实际上相当于 后缀 $[i + 2, n]$ 的长度为 $\frac{len}{2}$ 的前缀 拼上 后缀 $[i + 4, n]$ 的长度为 $\frac{len}{2}$ 的前缀。
而这两个前缀的信息已经在上一层处理过,那么可以直接拿来用。
说着确实很玄,放一张 oi-wiki 上的图应该就很清晰了。

$\mathbb{Code}$
1 | int n, m, w[MAXN], sa[MAXN], now[MAXN], rnk[MAXN], tmp[MAXN]; |
1 | void init(int n, int m) { // n 是 |string|, m 是字符集大小 |
1 | // 功能是排序装桶(对于当前) |
1 | void Sa(int n, int m) { |
The End「Ô mon âme, n'aspire pas à la vie immortelle, mais épuise le champ du possible.」