回文自动机与Lyndon分解

回文串

一个字符串中最多只能有 \(O(n)\) 种本质不同的回文串。

证明,考虑增加一个字符最多只会增加一个不同的回文串。

manacher 算法

利用回文扩展回文,均摊 \(O(n)\) 。

例题

用 manacher 求每个前缀的最长回文后缀。

题解

用 manacher 处理出回文串后,利用数据结构维护一下就可以了。

例题

用 manacher 求区间回文子串个数。

题解

你可以将所有点分类,然后分别求一下答案即可。

回文自动机

回文树的结构

  • 存在一个 \(-1\)​ 节点,和 \(0\) 节点分别存储长度为奇数和偶数的回文串。
  • 每一个节点代表一个回文串。
  • 每一个节点的父亲是该节点所代表的回文串的最长回文后缀。

由于本质不同的回文串只有 \(O(n)\) 个,所以节点数是 \(O(n)\) 个的,且于回文串一一对应,这与后缀树是不同的。

DAG 的结构

  • 每一个节点转移到首位各加一个该字符的节点。

回文自动机的建立

我们考虑上次添加的节点 \(\text{lst}\) ,即是在加入当前字符之前字符串的最长回文后缀。

我们考虑不停地寻找父亲直至于当前字符能够匹配上,此时去走转移,如果没有节点就新建一个节点。

对于当前节点,如果是新建的,我们考虑寻找他的父亲,方法类似于加入一个节点,也是不停地跳最长回文后缀去找。

感觉和 \(\text{SAM}\) 挺像的,但是简单很多。

性质

  • 两个图结构。

例题

在字符串前面加字符维护回文自动机。

题解

由于回文自动机是对称的,所以你可以直接搞双向添加,只要维护一下整个串的最长回文前缀的节点。

例题

求每个前缀的回文子串个数。

求区间的回文子串个数。

题解

回文树在建立过程中一个节点的祖先是不会变的。

每一个点新加进去的回文串数量就是它到根的深度。

不是很会?????????????????????

例题:LOJ6070 基因

求每个前缀的本质不同的回文子串个数。

求区间本质不同的回文子串个数。

题解

前缀的很简单,我们考虑一边建 \(\text{PAM}\) 一边输出节点个数。

区间的话,我们就直接考虑扫描线,同时维护每一个回文子串最后一次出现的位置,这个可以用 \(\text{LCT}\)​ 来维护,然后移动右端点查询左端点即可。这个好像还有很多细节,我不是很懂。


结合上一例题,直接回滚莫队?考虑到我们寻找父亲的复杂度是均摊的,这个可以利用倍增来将复杂度变正确。然后由于往右走是可以均摊的,所以我们调整一下块长就可以将复杂度有所优化?(没有变化)

Lyndon 分解

Lyndon 串

对于字符串 \(s\) ,如果 \(s\) 的字典序严格小于 \(s\) 的所有后缀的字典序,我们称 \(s\) 是简单串,或者 Lyndon 串。举一些例子,a,b,ab,aab,abb,ababb,abcd 都是 Lyndon 串。当且仅当 \(s\) 的字典序严格小于它的所有非平凡的循环同构串时, 才是 Lyndon 串。

两个 Lyndon 串的并(交非空)也是 Lyndon 串,所以我们定义极长 Lyndon 串,即不能继续与其他 Lyndon 串合并的 Lyndon 串。

Lyndon 分解

极长的 Lyndon 串,构成了 Lyndon 分解。这种分解可以看成三种等价形式:

  • 每次取最小后缀(极长后缀 Lyndon 串)的分解。
  • 每次取极长前缀 Lyndon 串的分解。
  • 是后缀数组的前缀极值。(方便且直观)

被 D 了。

上一篇:IOS出现Initializer element is not a compile-time constant 解决办法


下一篇:Android build.gradle配置文件