【北京集训】字符串
后缀数据结构
后缀数组、后缀树、后缀自动机
后缀树:
-
后缀 trie(将 \(s\) 的所有后缀插入 trie 中)的压缩形式
-
反串的后缀自动机的 link 树
-
一个节点代表一个 startpos 等价类,所有节点的等价类包含子节点 startpos 的并,且只有代表后缀的 \(n\) 个节点会在子节点 startpos 的并中加入当前后缀的 startpos。
后缀数组(\(sa\) 数组):
-
将 \(s\) 的所有后缀排序后的结果
-
后缀树的 dfs 序
-
height 即 dfs 序相邻的节点在后缀树上 LCA 所代表的最长串的长度
后缀自动机:
- DAG,这张 DAG 上所有从起点出发的路径都代表了一个 \(s\) 的本质不同子串
一般来说后缀树结构严格强于后缀数组,但是实际上把树拍到序列结构上之后也会有非常好的性质,例如可以快速二分哈希进行查找。后缀树结构上与哈希比较并不相容,这时后缀数组可以轻松解决。
经典题目
题目:CF666E
给你一个串 \(S\) 以及一个字符串数组 \(T_{1\ldots m}\),\(q\) 次询问,每次问 \(S\) 的子串 \(S[p_l\ldots p_r]\) 在 \(T_{l\ldots r}\) 中的哪个串里的出现次数最多,并输出出现次数。
\(|S|, q\leq 5\times 10^5\),\(\sum|T| \leq 5\times 10^4\)
先把 \(S\) 和所有 \(T\) 以特殊字符分隔连接成一个串 \(s\),对 \(s\) 建后缀树。
定位一个子串 \(S[l, r]\) 在后缀树上的位置:建后缀树时,记录 \(l\) 的后缀所在的节点,以及每个结点的 maxlen。倍增查找即可。
将 \(S[l,r]\) 的询问记在定位的后缀树节点上,离线处理。遍历后缀树,启发式合并处理出 \(c_i\) 表示 子树中所有 startpos 出现在串 \(i\) 中的个数,求最大值的编号即可回答询问。
题目:CF700E
给定一个字符串 \(S\),要求构造字符串序列 \(s_1,s_2,\ldots,s_k\),满足任意 \(s_i\) 都是 \(S\) 的子串,且任意 \(i\in[2,n]\),都有 \(s_{i-1}\) 在 \(s_i\) 中出现了至少 \(2\) 次。求可能的最大的 \(k\) 的值(即序列的最大可能长度)。
\(n\le 2\times 10^5\)。
考虑一个合法的序列 \(s\),我们一定可以使得 \(s_{i-1}\) 为 \(s_i\) 的 border,不会更劣。但是 border 在后缀数据结构上不太好刻画,因此 放宽条件,只考虑 \(s_{i-1}\) 一定为 \(s_i\) 的前缀这个性质。
在后缀树上进行 dp,\(f_i\) 代表节点 \(i\) 所代表的子串至多可以作为 \(s_{f_i}\)。记录当前祖先链上 \(f_j\) 最大且所代表的串最短的 \(j\)(以及这个最短串的长度),判断 \(j\) 串是否在 \(i\) 串出现两次即可。具体地,可以记录 \(j\) 的次小 startpos 看是否在 \(i\) 串中。
题目:CF1608G
给定 \(m\) 个仅包含小写字母的字符串和一棵包含 \(n\) 个节点的树,树的每条边上都有一个小写字母。我们定义 \(str(u,v)\) 为将在树上从 \(u\) 到 \(v\) 的路径上经过的所有边上的小写字母顺此拼接得到的字符串。你需要回答 \(q\) 次询问,每次询问给定 \(4\) 个整数 \(u,v,l,r\),你需要回答在所有下标在 \([l,r]\) 的字符串中,\(str(u,v)\) 出现了多少次。
\(n, m, q\le 10^5\)。
SA 好题。
先把 \(m\) 个字符串用特殊字符拼接。
这个时候要匹配的串变成了树路径,后缀树和 SAM 都不太方便(若不逐位匹配就要使用 DAG 链剖分一类的科技),考虑我们只需要知道其 startpos 集合,不妨用序列也就是 SA 来应对。
考虑在 SA 上二分出以该路径形成的字符串为前缀的串的区间。若字符串比较时直接使用二分+哈希套树上倍增就会得到 \(3\log\) 做法,非常不优。
但二分和倍增的结构实际上是非常相似的,因此直接用倍增直接拼出相等前缀即可。
二分出 \(sa\) 数组上的区间 \([L, R]\) 后,查询其中有多少数在询问的 \([l, r]\) 之间,扫描线即可。复杂度 \(O(q\log^2 n + n\log n)\)。
SA 在 定位字符串 时有比 SAM 更加好做的方法:配合哈希进行二分。
题目:CF1801G
给你一个字符串 \(t\) 和 \(n\) 个字符串 \(s_1,s_2,s_3,\dots,s_n\)。
统计满足 \(\exist j, t[a,b]=s_j\) 且 \(l_i \leq a \leq b \leq r_i\) 的 \((a,b)\) 的个数。
\(\lvert t \rvert \leq 5 \times 10^6\),\(\sum_{i=1}^n{\lvert s_i\rvert} \leq 10^6\)
其实放在这里并不合适,是一道 ACAM 题。
但是提供了想法:在计算区间相关的匹配数时,可以先用前缀相减,再减去跨过边界两边匹配的。
跨边情况建立正反两个 ACAM 并离线查询即可。
例题
还没补。

浙公网安备 33010602011771号