【学习笔记】后缀自动机(SAM)
后缀自动机
可怜的我,学了两三遍才终于有点感觉了,也是因为这东西是真的复杂。
基本定义
后缀自动机(SAM)是用来解决字符串问题的一种数据结构,可以理解为字符串的压缩形式,是一个有向无环图。其中从源点出发到达每一个点的路径都是原串的一个子串,且原串的每一个子串都一定可以表示为一条路径。
endpos
定义
在字符串 中,我们记 为字符串 在 中所有的结束位置。例如在字符串 "" 中 。
我们就将 值相同的字符串称作一个等价类,即在同一个等价类中必定存在 ,其中 为两个字符串
SAM 中的每个节点对应一个等价类,即 SAM 节点的数量为等价类的数量加一,因为存在一个初始状态。
性质
性质一:若存在 ,且满足 ,即在字符串 中, 都是以 的后缀的形式出现。
性质二:对于每一个 等价类,等价类中的子串长度一定恰好完全覆盖 , 为最短子串长度, 为最长子串长度。且任取两个子串,较短者一定是较长者的后缀。
后缀链接(link)
定义
对于一个状态 ,我们记 为其的最长子串,那么一个后缀链接就是由 连向 的所有后缀中与 的 值不同且最长的子串的状态。(好绕啊)
性质
性质一: 所有后缀链接构成一棵以源点为根的树
边也就是后缀链接,把所有的边反向也就是一颗以源点为根的树
(转自 OI_Wiki)
构造
SAM 的构造看上去很简单,但实际上非常难以理解。
(转自 OI_Wiki)
下面就是详细地解释一下这个东西。
SAM 的构造是在线的,也就是说我们是在原有的字符串末尾一点点插入字符然后构造的。
第一个点:没有什么好说的
第二个点: 即 这个节点代表的状态下的最长子串的长度,新加入的这个状态代表的最长子串也就是整个字符串,长度也就是没有这个点的时候的字符串长度加一
第三个点:其实也没啥说的,就是找到一个状态 ,使得 是从 沿后缀链接向前第一个遇到的有字符 的转移的状态,转移可以理解为从这个状态的最长子串后面加一个 能得到的子串所对应的状态。
第四个点:所有的能从 沿后缀链接能到的状态都没有字符 的转移,也就是原串里加入字符 之前,不含有字符 ,那么根据后缀链接的定义,连接任何状态都不对,所以只能与源点项链。所谓从 沿后缀链接能到的状态,可以理解为其的子串一定包含原串的所有后缀,但不仅包含这些而已。
第五个点:没啥说的,其实就是找到一个状态 使得 是 的最长子串后加入一个字符 得到的状态。
第六个点:略过
第七个点:根据后缀链接的定义,那么对于 中的最长子串一定是不加入字符串 的原串的后缀,那么其加入一个字符 之后就一定是新串的后缀,而 又是第一个找到的,也就是满足该条件的状态里最长字串最长的一个,那么在 后面加入一个字符 ,也就可以理解为新串的后缀,也就是 状态,而 一定不等于 ,而 又是最长的一个满足条件的新串的后缀,也就是 的后缀,满足后缀链接的定义,所以应该从 后缀链接到 。
第八个点:这里前提条件就是 含有比 更长的一个字符串,但是考虑一点 是新串的后缀,而 与 的 相同,也就是说 里面包含的这个最长的字符串也肯定是新串的后缀。下面我们就考虑分出来这样的一个点,也就是 ,使得 是类似第七个点情况下的 ,所以这样的话就应该使得 连向 , 连向 ,根据后缀链接的定义, 肯定是 中的后缀,而且它们的 一定不同,所以这样链接。这样的话加上字符 的转移就有了更加好的去处,也就是
第九个点:略过。
可能是相当的不严谨,但是我们也没必要有那么优秀的证明能力,能有自己理解的逻辑应该就够了。
时间复杂度:
用处:
(OI_Wiki 上的加上我的理解)
(1)检查一个字符串是否出现过:
因为原串的每一个子串都一定是从源点出发的一条路径,所以就从源点扫就好了
(2) 询问不同子串个数:
1.我们的 SAM 是一个有向无环图,因为每一条路径都对应原串的每一个子串,所以就相当于统计路径条数, DP 即可
2.考虑 SAM 本身的定义: SAM 上的节点定义为不同的 等价类,那么我们只要能求出所有等价类的对应的子串的数量然后求和即可。对于同一 等价类中的子串,其长度一定恰好完全覆盖 ,因为我们的后缀链接存在一个性质,,根据定义也非常好理解
(3)所有不同子串的总长度:
1.利用有向无环图, DP 就好了
(4)字符串第 大子串:
字符串第 大子串即 SAM 上的第 大路径,求出每个状态的路径条数,然后找就可以了
(5)最小循环移位:
这个字符串里长度为 的一段,一定对应着 的一个循环进位,就对 建 SAM 然后贪心选择最小的长度为 的一段就好了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)