01.23 算法补全:后缀数组

秉着技多不压身的想法,我认为在有些时候后缀数组的直接建法还是有用处的,于是决定快速地补一下这个算法。

以后看看能不能每天稳定产出一篇,随便什么的文章。可能是一个 trick 的记录,也能是算法补全,或者是题解慢报/速报,亦或是鲜花。这些内容会同步发表于我的洛谷 blog:https://www.luogu.com.cn/blog/forever-captain/,标签为【高二 OIer 精神状况记录】

1. 后缀数组的构建

考虑如下排序方法:我们从 \(h=0\to \lceil\log_2n\rceil\),对于每个 长 \(2^h\) 的子串(末尾用 \(0\) 补齐)进行离散化操作。

这个是容易的。我们得到了长 \(2^h\) 的子串的排名 \(f(S)\) 后,设一个长 \(2^{h+1}\) 的子串由长 \(2^h\) 的子串 \(A\)\(B\) 拼接而成,那么令这个子串的权值为二元组 \((f(A),f(B))\) 即可。这个二元组的排序可以使用基数排序:先按第二关键词排,再按第一关键词排。

实现:https://loj.ac/s/1985591

然后考虑一个非常简单的问题:求任意两个后缀的 LCP。考虑设如下两个数组:\(rk_i\) 表示后缀 \(i\) 的排名,\(h_i\) 表示 \(\operatorname{LCP}(sa_{i-1},sa_{i})\)。于是两个后缀 \(x,y\) 的 LCP 即 \(h[x+1,y]\) 的最小值。

\(a_i=h_{rk_i}\)(即位置为 \(i\) 的后缀的 \(h\) 值),于是有性质 \(a_i\ge a_{i-1}+1\)。这个性质是很直观的,不再作证明。于是可以直接求 \(h\) 了。

2. 树上后缀排序

后缀平衡树能直接做。不多赘述这个比较无脑的做法。

后缀数组做法:考虑修改一下序列的后缀排序,让其上树。倍增可以直接改成树上倍增,其实就好了。

但是考虑到树上可能会存在两个串相同的情况,这个是容易的,先做一次后缀排序之后,再判断相同即可。注意判断相同需要对于离散化值相同的还需判深度相同。

3. 【例】NOI2023 字符串

这题主要应用 rk 数组。rk 数组在子串比较中是有很大的用处的。

考虑把串反过来接到原串后面形成 \(S'\),那么题目的限制变为 \(S'[i,i+l-1]\)\(S'[2n-i-2l+2,2n-i-l+1]\) 小。对于特殊性质 B,这个等价于后缀 \(i\) 小于后缀 \(2n-i-2l+2\)。直接对 rk 数组分奇偶二维数点即可。对于有相邻的相同的情况,可以发现我们多算了满足,以 \((i+l-1,i+l)\) 为回文中心且覆盖到 \(i\) 且后缀 \(i-1\) 小于后缀 \(2n-i+1\) 的情况。考虑 Manacher 跑出回文后,同样是一个二维数点问题。所以总复杂度 \(O(n\log n)\)

注意:多次 Manacher 一定要把辅助串清得干干净净。

实现:https://uoj.ac/submission/674818

posted @ 2024-01-23 17:18  LarsWerner  阅读(17)  评论(0编辑  收藏  举报