后缀自动机 SAM
后缀自动机SAM #算法
SAM满足如下性质
- 有向无环图 每个转移只有一个字符
- 接受且只接受 的后缀
- 节点数在满足上述条件下最小
考虑不满足性质 ,那么 就可以做到
将这个 建出来后,发现有很多完全相同的子树
定义 表示串 在 中所有匹配的结束位置的集合,对于所有的 ,可以考虑把这两个点合起来 的数量是 的 ,可以发现每个集合都是由一个更大的集合划分得到的,形成一个树形结构,每个节点表示一个 ,一般将这个树称为 ,这棵树在满二叉树时达到 个点
对于构建,维护 表示当前状态接受 后转移到的状态, 为当前节点在 上的父亲节点, 表示当前这个 下最长的串的长度, 表示加入之前的状态
对于插入一个字符 ,新建一个节点 ,然后沿着 向上找,如果一个点不能向 转移,那么这个点的
- 如果到根都没有找到,那么将根指向 即可
- 若找到的点为 , 的
- 若 ,那么 即可
- 否则就说明当前转移不连续,需要将它拆开,将所有 即以上的可以通过 转移的节点连向一个新的节点 ,然后将 指向 和
namespace SAM { const int MAXM = 1e6 + 5; struct State { int fa, len, next[26]; } sam[MAXM]; int cnt = 1, last = 1; void insert(int ch) { // 插入时要-'a'(或其他) int cur = ++cnt, p; sam[cur].len = sam[last].len + 1; for (p = last; p && !sam[p].next[ch]; p = sam[p].fa) sam[p].next[ch] = cur; int q = sam[p].next[ch]; if (q == 0) { sam[cur].fa = 1; } else if (sam[p].len + 1 == sam[q].len) { sam[cur].fa = q; } else { int r = ++cnt; sam[r] = sam[q]; sam[r].len = sam[p].len + 1; for (; p && sam[p].next[ch] == q; p = sam[p].fa) sam[p].next[ch] = r; sam[cur].fa = sam[q].fa = r; } last = cur; } } // namespace SAM
本文作者:xiaruize's Blog
本文链接:https://www.cnblogs.com/xiaruize/p/18105724
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步