- 概念理解与经典类型
- [BZOJ1398] 寻找主人 Necklace
- [SP7258] SUBLEX
- [HDU5769] Substring
- [HDU4436] str2int
- [SP1811] LCS
- [LOJ171] 最长公共子串
- [SDOI2016] 生成魔咒
- [JSOI2012] 玄武密码
- [BJWC2010] 外星联络
- [Usaco2006 Dec] Milk Patterns
- [HDU4622] Reincarnation
- [HDU4416] Good Article Good sentence
- [BZOJ2780] Sevenk Love Oimaster
- [Usaco10Dec] Threatening Letter G
- 综合运用
- 广义 SAM 应用
概念理解与经典类型
[BZOJ1398] 寻找主人 Necklace
给定 (S,T),判断其是否循环同构,如果是则输出最小表示。
直接用 SAM 求最小表示比较是否相等即可。
[SP7258] SUBLEX
求给定主串的所有本质不同子串中第 (k) 小子串。
本质不同子串集合和 SAM 的转移边路径集合构成双射,按拓扑逆序计算每个点发出的路径数,跑 kth 即可。
[HDU5769] Substring
求一个串种包含某个特定字符的本质不同子串有多少种。
设 f[p] 表示从结点 p 出发沿着转移边走能到达的合法的本质不同子串有多少种,设题中要走的字母为 c,对于 p 通过 c 转移到 q,f[p]+=siz[q];对于其它字符,f[p]+=f[q],其中 siz[p] 表示 p 出发可以转移到的本质不同子串数目,按照拓扑逆序统计即可。
[HDU4436] str2int
给定若干字符串,每个字符串中所有子串转化为数字,放在一起去重后,求它们的和 (mod 2012)。
如果能处理好前导零那么就是本质不同子串化数求和。将各个数字串用奇怪字符连接起来建立 SAM,对 i 设从根到达它的路径数量为 f[i],它代表的本质不同子串转化为数字后的和为 g[i],对于 i 经过 c 到 j,有 f[j]+=f[i], g[j]+=10g[i]+c*f[i]。为了处理前导零,在根结点转移出去时,跳过 0 转移即可。
[SP1811] LCS
求两个字符串的最长公共子串。
对 A 建立 SAM,将 B 扔到上面跑,记录当前结点 (p) 和匹配长度 (l),能走 trans 边则走,否则沿着 link 跳并让 (l) 对 (len[p]) 取 (min),则 (l) 的历史最大值即为答案。
[LOJ171] 最长公共子串
求多个字符串的最长公共子串。
沿用 [SP1811] 的思路,选出最短串建 SAM,对每个节点记录各个串跑时匹配长度的最小值。
每个串跑时,节点先记录本轮匹配的最大值,再按拓扑逆序上传,最后更新记录的最小值。
[SDOI2016] 生成魔咒
按顺序在一个序列的末尾插入数字,求每次插入后的本质不同子串个数。
后缀自动机增量构造时,已经有的结点的 len 是不会再变化的,而 nq 结点不会产生贡献,于是每次加上 len[last]-len[link[last]] 即可。
[JSOI2012] 玄武密码
给定一个基础串 S 和若干询问串 T,对于每个 T 求出其最长前缀 P 满足 P 是 S 的子串。
SAM 是能接受所有 S 子串的自动机,因此将每个 T 丢上去跑,只能走转移边,走不动时就是答案
[BJWC2010] 外星联络
求字符串中出现次数大于 1 的所有本质不同子串的出现次数。
建出 SAM 后 DFS 并输出 endpos 集合大小。
[Usaco2006 Dec] Milk Patterns
给定串 (S) 求出现至少 (k) 次的子串的最大长度。
出现次数即 endpos 集合大小,基数排序预处理即可。所有满足条件的节点 len 中取最大即可。
[HDU4622] Reincarnation
区间询问本质不同子串个数。要求 (O(n^2))。
暴力枚举起点,增量构造,即得到所有区间的答案。(优化做法见后)
[HDU4416] Good Article Good sentence
给定 (S) 和若干模式串 (T_i),问 (S) 的子串中有多少不是任何一个 (T_i) 的子串。
对 S 建立 SAM,(T_i) 跑到 p 时,匹配长度为 l,则该结点及其后缀链接上所有结点所表示的长度 (le l) 的子串都应当被抛弃。考虑对每个结点维护一个匹配过的最大长度 f[i],每次在匹配位置标记,最后按拓扑逆序上传取 max。统计 i 点的答案时,贡献为 len[i]-max(f[i],minlen[i])。
[BZOJ2780] Sevenk Love Oimaster
给定若干主串和若干询问,每次询问一个字符串在多少个主串中作为子串出现过。
对主串构建广义 SAM,求出每个结点在多少个主串中出现过,记为 f[i]。求解时可以暴力沿着后缀链接跳,但为了防止记重同时降低复杂度,需要记录每个结点在本轮中是否被访问过,碰到访问过的就结束。对于询问串扔到自动机上跑,设跑到了 p 则答案为 f[p]。
[Usaco10Dec] Threatening Letter G
给定 (s_1,s_2),问至少需要多少个 (s_1) 的子串才能拼接成 (s_2)。
贪心,对 (s_2) 从左到右扫,能分在上一段里必然不会更劣,于是让 (s_2) 在 (s_1) 的 SAM 上贪心地沿着转移边跑,跑不动了就记答案并回到根。
综合运用
[BZOJ2555] SubString
给定一个初始串,支持两种操作:在当前串后面加上一个串;询问一个串在当前串中作为子串出现次数。
询问即后缀链接树的子树大小,LCT 维护子树大小即可。
Wannafly Camp 2020 Day 2D 卡拉巴什的字符串
动态维护任意两个后缀的 (lcp) 集合的 (mex),支持在串末尾追加字符。
显然答案单调增。LCP 即后缀链接上的 LCA 的 maxlen,一个结点可能成为 LCP 当且仅当它有儿子。修改父子关系时在桶上暴力维护即可。
[BZOJ1396] 识别子串
给定 (S),问其每一位的最短识别子串是多长,即包含这个字符且仅出现一次的串。
建出 SAM,只有 endpos 大小为 1 的结点才有贡献,其范围一定是 [i,pos],对 [pos-minlen+1,pos] 中的每个点产生 minlen 的贡献,对 [pos-maxlen+1,pos-minlen] 中的每个点产生 pos-i+1 的贡献。第一部分线段树直接维护,第二部分线段树维护 f[i]-i 即可。
[CF149E] Martian Strings
给定主串 S 和若干询问,每次给定询问串 p,输出有多少个询问串,满足存在 S 的两个不相交的子串拼起来与 p 相等。
询问串可行当且仅当存在 k 使得 p[1..k] 在主串中的最小结束位置 pre[k] 小于 p[k+1,l] 的最大开始位置 suf[k+1]。对 S 正反串分别建 SAM,通过按拓扑逆序上传预处理每个结点对应的 endpos 集合的最小值。将 p 扔到 SAM 上跑,只走转移边不走后缀链接,即可求出 pre,suf。
[BZOJ2119] 股市的预测
寻找 (ABA) 形式的子串数目,满足 (|B|=m)。
枚举 (A) 长度 (i),将 (i,2i,3i,...) 设置为关键点,分别考虑左侧的 (A) 横跨每一个关键点时的情况,由于 (A) 会且仅会横跨一个关键点,这样统计是不重不漏的。设枚举的关键点为 (p),则右侧 (A) 上与之对应的是 (p+i+m),求出这两点的 (lcp,lcs),则左侧 (A) 块可以是区间 ([p-lcs+1,p+lcp-1]),故可能位置有 (lcs+lcp-i) 种。为了不重复,(lcs,lcp) 要对 (i) 取 min。求 (lcs,lcp) 用 (SA) 或 (SAM) 上求 (LCA) 均可。
[P6292] 区间本质不同子串个数
区间询问本质不同子串个数。要求 (O(n log^2 n))。
考虑离线,将所有询问挂在右端点上,从左到右扫描整个序列,设扫描到的位置为 (r)。用线段树统计答案,第 i 位表示 S[1..r] 中最后一次出现在左端点为 i 位置的子串数,询问就是求 [l,r] 和。在新增字符 S[r] 时,会对 [1,r] 各产生 1 的贡献,由于某些 S[i..r] 在之前出现,为了去重,用 SAM 维护串,从 S[1..r] 对应节点沿着后缀链接跳,用经过所有节点上一次出现的右端点 lastpos 计算出发生重复的区间 [lastpos[p]-len[p]+1,lastpos-len[fa[p]]],在线段树上 -1,并将 lastpos 设置为 r。考虑到 lastpos 相同的点可以一起处理,每次从当前位置跳到根的过程就是 Access 的过程,用 LCT 维护即可。
广义 SAM 应用
[P6139] 【模板】广义后缀自动机
给定 (s_1,s_2,...,s_n) 求本质不同子串个数。
广义后缀自动机不只是将 (last) 指回 (root)。建好自动机后像普通 SAM 那样统计答案。
[ZJOI2015] 诸神眷顾的幻想乡
给定一棵叶子结点数量 (le 20) 的树,在树上任选一条路径,问有多少种颜色序列不同的选择。
把每个叶子为根的树 DFS 一遍,当做 Trie 插入广义后缀自动机即可。
[CF427D] Match & Catch
求在两个给定字符串中各只出现一次的最短公共子串。
建立广义 SAM,对每个结点记录来源于各个串的 endpos 集合的大小,如果同为 1 则更新答案。