这是一篇其实理解并不深刻只会打板子的蒟蒻写出的总结
分成几个板块吧。。。。。。
- 1.检查字符串是否出现
给一个文本串 (T) 和多个模式串 (P),我们要检查字符串 (P) 是否作为 (T) 的一个子串出现。
对 (T) 建出 (SAM)
直接在后缀树上从根开始往下走,如果能走到 (P) 结尾说明是模板串的子串
- 2.不同子串个数
给一个字符串 (S),计算不同子串的个数。
静态:我们知道每个子串就是后缀DAG上的一条路径
DAG上路径数怎么统计就不用说了吧
动态:每次新建一个节点,贡献为(len(np)-len(f(np)))
好像挺显然的?
例题:生成魔咒
- 3.最小循环移位
给定一个字符串 (S) 。找出字典序最小的循环移位
发现字符串 (S+S) 包含字符串 (S) 的所有循环移位作为子串。
所以问题变为在 (S+S) 对应的后缀自动机上寻找最小的长度为 |S| 的路径
直接从初始状态开始,贪心地访问最小的字符即可。
例题:工艺
- 4.出现次数
对于一个给定的文本串 (T) ,有多组询问,每组询问给一个模式串 (P) ,回答模式串 (P) 在字符串 (T) 中作为子串出现了多少次
我们发现用如果模式串在 (SAM) 上跑匹配,那么最终到达的点的 (endpos) 就是该串的出现次数
考虑 (endpos) 的处理根据定义发现其实就是后缀树上的子树大小
所以我们把实点权值设为 (1),虚点设为 (0),跑拓扑就行了
例题:(substring)(需要动态维护 (endpos),打棵 (lct) 呗)
- 5.字典序第 k 大子串
给定一个字符串 (S) 。多组询问,每组询问给定一个数 K ,查询 (S) 的所有子串中字典序第 K 大的子串。
字典序第 (K) 大的子串对应于 (SAM) 中字典序第 (k) 大的路径
那么在计算每个状态的路径数后,就可以从 (SAM) 的根开始找到第 (k) 大的路径。
例题:弦论
- 6.第一次出现的位置
给定一个文本串 (T) ,多组查询。每次查询字符串 (P) 在字符串 (T) 中第一次出现的位置( (P) 的开头位置)。
预处理出每个状态第一次出现的位置(pos(i))
其实只需要让每次(pos(np)=len(np) pos(nq)=pos(q))就好了
查询答案为(pos(i)-|T|+1)
- 7.最短的没有出现的字符串
给定一个字符串 (S) 和一个特定的字符集 (T),我们要找一个长度最短的没有在 (S) 中出现过的字符串
在 (SAM) 上做 (dp)
设 (dp_i) 表示到点 (i) 时的最短长度
如果这个点有不是 (T) 中字符的出边,则 (dp_i=1),否则 (dp_i=1+minlimits_{(i,j,c)in SAM}dp_j)
- 8.两个字符串的最长公共子串
给定两个字符串 (S) 和 (T) ,求出最长公共子串。
直接把 (T) 扔到 (S) 的自动机上跑匹配就行了
- 9.求 (endpos) 集合
给定一个字符串 (S),求 (endpos) 集合
首先我们能够求出每个节点的 (pos)
然后发现一个点的 (endpos) 就是他子树的 (pos) 的集合
怎么让一个点带上整个子树的某个值? 主席树合并啊!
每个点初始在主席树上插入 (pos(i))
然后拓扑合并就行了
例题:你的名字