正向思维理解后缀自动机

大部分网上对SAM 的讲解逻辑很混乱,让我对 SAM 只能知其然,而不知其所以然,所以在这里理清了 SAM 的逻辑链

前置知识 : DFA#

建议对 SAM 有个基本概念再来看此文
注意:有什么没写明的符号,在参考文献1里面已经说明

后缀自动机的定义#

后缀自动机(SAM)就是接受一个字符串所有后缀的最小DFA。
但是这个定义过于迷惑,我们考虑换一种等价定义:
SAM是以 endpos 等价类为状态的,接受所有后缀的DFA。
endpos 将在下文说明)
证明定义的等价性:

  • 可以构造出一个以 endpos 等价类为状态的,接受所有后缀的DFA
  • 这个DFA是最小的 (此处证明较为复杂,略去)

这个定义修改是关键的,之后的一切操作、性质都以新定义位出发点

后缀自动机的性质#

  • 是证明边 O(n) 的前置知识
    建议理解 endpos 等价类后再来理解这里

在下文讲解中,我们默认"路径”是指从源点(起始状态)出发的路径

  • 性质1
    每条终点为接受状态的路径均代表一个后缀
  • 性质2
    每条路径均代表一个子串
  • 性质3
    不同路径代表的子串是本质不同的

后缀自动机的理论基础#

endpos 及其等价类的性质#

endpos(str) 是一个从字符串子串到一个集合的映射
它表明一个子串在字符串中每次出现的结束位置
endpos 等价类是所有 endpos 相同的子串
具体地,对aabab这个字符串的子串ab来说
endpos("ab")={3,5}
(在本文中,我们默认字符串首字母的下标是1)
下面的性质讲不给出证明,请见互联网

  • 性质1
    如果两个子串的 endpos 相同,一个字符串是另一个的后缀
  • 性质2
    两个子串的 endpos 只有相交或包含关系
  • 性质3
    一个 endpos 等价类里面所有子串长度连续
  • 性质4
    endpos 等价类的个数 O(n)
  • 性质5
    endpos 等价类中的所有子串同时在末尾添加一个字母得到的子串,均为同一个等价类

其中,
性质1、2为性质3提供证明
性质2 暗示我们,所有 endpos 等价类可以构成一棵树, 我们把这棵树叫做 parent_tree
性质4 证明了 SAM 的状态数为 O(n)
性质5 解释了 SAM 的转移,告诉我们 SAM 是存在的

后缀自动机的状态数 O(n)#

性质4 上文已经给出证明

后缀自动机的转移数 O(n)#

我们考虑 SAM 的一颗生成树
从每一个 SAM 的接受状态(代表了至少一个后缀)出发,按照后缀转移路径的相反顺序,去向源点反向转移,在反向转移中我们需要添加一些非树边,我们只要证明非树边的条数 O(n) 即可
其它子串是后缀的前缀,只要后缀满足路径可行,其它子串也满足路径可行
为了证明这个结论,我们可以证明对每一个后缀,我们只需要均摊添加不超过一个边即可
换句话说,我们可以证明每连一条边,必然能够反向转移一个新的后缀
需要连接 1条边的后缀是显然的,我们只考虑这样一个后缀,它需要连接 >1条边,我们先任意的连其路径上的一条边,然后不管它,去沿着到源点的路径反向转移
在连接一条新边后,因为这是一颗生成树,所以其到源点的路径一定是联通的,并且这条路径包含一个新边,所以其一定代表这一个新的后缀,我们证明了每连一条边,必然能够反向转移一个新的后缀

后缀自动机的构造#

因为每个字符串前缀的 endpos 必然不相等,而且能从较短前缀转移到较长前缀,所以我们可以把 SAM 中代表前缀的若干个状态提出来,叫做前缀链
这样就把 SAM 分成两部分,前缀链和其它
我们每次在字符串结尾添加一个字符,维护 SAM ,最终即可做到 SAM 的构造
我们在添加一个字符之后,有一些字符串的 endpos 改变了,即状态的含义改变了,还有一些字符串的转移的 endpos改变了,即转移后的新状态的含义改变了, 我们必须重新进行维护 parent_tree(即fa数组) 和 SAM
设新字符为 c ,新串长度为 n
哪些字符串的 endpos 改变了?
只有原来 后缀+ "c" 字符串的 endpos改变了, 即比原先多了一个 n
现在就有一个很自然地(不完善的)思路,
我们从沿着 last向上跳到空状态,不难发现整条链都是原串后缀,我们将这条链称之为后缀链
我们就向上遍历后缀链,然后考察这个状态是否有 c 的转移,如果没有 c的转移,这就说明这个 该状态的字符串 + c 在旧串中是不存在的,不过现在随着新结点的加入而存在,我们可以直接连到新结点上
如果有c的转移,就说明这个字符串 +c在旧串中是存在的,那么它的后缀一定也有c的转移。
从而,我们现在就可以将新结点的fa设成第一个有c转移的状态的的c转移,这表明以新结点为叶子的链构成了一个新的后缀链。换句话说,新结点的fa是旧串中为新串后缀的最长子串。
我们到这里便找到了新结点的fa
但是,这个思路是错误的,在“从而”后面,我们少讨论了一种情况,旧串后缀 + c作为其它串的后缀了,且它们endpos相同,这个时候只有旧串后缀 + cendpos包含 n, 而其它串不含n,我们不得不把这个状态分裂,并且重新调整fa和转移
具体的aaba + b
"a"是旧串一个后缀,"a"+"b",是新串的一个后缀,endpos("aab")={3} endpos("ab")={3,5}
所以此时"aab""ab"在一个状态里,我们只能将其分裂来保证 SAM的正确性
详细构造请见互联网

参考文献
https://www.luogu.com.cn/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie
https://oi-wiki.org//string/sam/#link

作者:cdsidi

出处:https://www.cnblogs.com/cdsidi/p/16440571.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   CDsidi  阅读(139)  评论(9编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu