[学习笔记 #9] 回文自动机和“广义回文自动机”
[学习笔记 #9] 回文自动机和“广义回文自动机”
[ ] 里的是我还不确定的。
回文自动机(PAM)
维护一个字符串的所有本质不同回文子串。
增量构建,每次在结尾处加一个字符,考虑会增加多少本质不同的回文子串,显然增加的是以它结尾的回文子串。把所有以它结尾的回文子串都拿出来,找到最长的那个,那么其他的都可以对称过去,显然对称过去还是回文的,且在没有加这个字符的字符串内,那么这些回文子串就已经被统计过了。所以每次添加的新的本质不同回文子串只可能是最长的那个。
PAM 由两部分构成,一部分是一棵基环树(唯一的环是奇根的自环),一部分是一张 DAG,而这两部分的结点都是共用的。
有两个点:奇根(编号为 \(1\))和偶根(编号为 \(0\))。其余的每个点表示一个回文子串,所有点对应的回文子串本质不同。
基环树上某个点连向父亲的边表示这个点的回文子串的最长回文 真 后缀,类似于 KMP 和 ACAM 的 fail。DAG 上的边带有字符,表示在某个串的开头和结尾各添加一个这个字符,类似于 Trie 和 SAM(SAM 的 DAG)上的边。
在结尾处加一个字符有两个步骤:
- 首先找到以这个点结尾的最长回文子串(原串的最长回文后缀),判断是否需要添加新点。
- 添加新点,找它的最长回文真后缀去更新。
- 需要注意的是,这一步是基于上一步找到的结点的。最好开一个函数来写这一步,防止这个点被改了,而之后还想用原来的值。
- 另外需要注意奇根的问题,要连到偶根来给之后的点机会。(具体见代码。)
这样在结尾一个个添加字符就把 PAM 建完了。
类似 ACAM,一个点在基环树上到根的链上的所有点依次是这个点的表示的子串的所有回文后缀。
“广义回文自动机”
“广义回文自动机”支持维护多个字符串的本质不同回文子串(指的是每个字符串的所有回文子串丢到一起再去重后的东西)。
类似广义后缀自动机,有离线构建和在线构建两种方式。
注意:我还没写过(2024.12.7),在线构建是我看别人的博客理解的,离线构建(Trie)是我口胡的,都不清楚我想得对不对。
在线构建
[因为 PAM 的构建过程中会判重,[每个结点又只表示一个回文子串],所以直接依次插入每个字符串,插入每个字符串前都把 lst 初始化为 \(0\) 即可。其他和 PAM 没有区别。]
from 广义回文自动机 - violetctl39 - 博客园 (cnblogs.com).
离线构建(Trie)
先把所有字符串建到一棵 Trie 里。
和离线建广义 SAM [的一种方式] 类似,在 Trie 上 BFS 来建,每次的 lst 是它的父结点所对应的结点。注意细节,应该和这种建广义 SAM 的方式要注意的细节差不多。
相比在线构建的优势在于 [时间复杂度是 Trie 结点数而不是串长之和]。
2024.12.7