后缀数组 SA
后缀树组(SA,suffix array),用于处理字符串子串形成的结构。
处理子串的结构主要方式有:后缀数组 SA,后缀自动机 SAM,后缀树 ST。
后缀树和后缀自动机暂时决定咕咕咕,以后学习可以参考ix35 的字符串复习。
含义与实现
后缀
我们定义长度为 \(n\) 的字符串 \(s\) 编号为 \(l\) 的后缀为 \(s\) 在 \([l,s]\) 上的字串,记 \(l\)(即起始位置)为该后缀的编号。
后缀树组
如果将 \(s\) 所有 \(n\) 个后缀提取出来,按照字典序升序排序,后缀数组 \(\text{SA}_i\) 定义为排名为 \(i\) 的后缀的编号。
那么怎么求出一个字符串的后缀数组呢?
求解
显然暴力找出 \(n\) 个后缀再快速排序的 \(\mathcal{O(n^2\log n)}\) 的(加上比较的复杂度),不够优美。
一般采用 Manber 和 Myers 发明的类似于倍增的方法求解。
(下面图片来自jinkun113 - 后缀数组,如有侵权请告知删除)
如上图,类似每次将两端字符串拼起来形成新的字符串,类似高低进制拼接后再一次排序得到。
通过这样的操作,以 \(i\) 为开头的字串会不断增长到两倍(大于 \(n\) 的用空字符补齐),即不断溢出,最终就得到了 \(n\) 个后缀。
类似这样的倍增求解的方法还可以用于其他的问题,如CF1654F Minimal String Xoration。
实现
在上面的过程中,记在 \(i\) 位置上合并的两个数高位的是 \(x_i\),低位的是 \(y_i\),他们记录的是这一部分字串的排名,最终再一次排序后的排名记在 \(x\) 数组内。
这样非常容易写出一个 \(\mathcal{O(n\log^2 n)}\) 的解法,具体就是每次将两端字串合并后,给 \(i\) 赋值为 \(x_i,y_i\) 高低位相接。之后再一次排序并离散化就可以得到新的排名。
//暴力O(n\log^2 n)
int MAX;
int sa[Maxn],First[Maxn],Secon