SAM(后缀自动机)专题总结

这是一篇其实理解并不深刻只会打板子的蒟蒻写出的总结

分成几个板块吧。。。。。。

  • 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+\min\limits_{(i,j,c)\in SAM}dp_j\)

  • 8.两个字符串的最长公共子串

    给定两个字符串 \(S\)\(T\) ,求出最长公共子串。

直接把 \(T\) 扔到 \(S\) 的自动机上跑匹配就行了

  • 9.求 \(endpos\) 集合

    给定一个字符串 \(S\),求 \(endpos\) 集合

首先我们能够求出每个节点的 \(pos\)

然后发现一个点的 \(endpos\) 就是他子树的 \(pos\) 的集合

怎么让一个点带上整个子树的某个值? 主席树合并啊!

每个点初始在主席树上插入 \(pos(i)\)

然后拓扑合并就行了

  例题:你的名字

猜你喜欢

转载自www.cnblogs.com/mikufun-hzoi-cpp/p/12098738.html