回文自动机

前言
给定一个字符串
。保证每个字符为小写字母。对于 的每个位置,请求出以该位置结尾的回文子串个数。 强制在线。需要算出
后才能解密 。
如果使用暴力做法,甚至 Manacher,时间复杂度肯定会至少
所以,我们需要用另外一种方法来维护目前所有的回文字符串。使用即将要讲的回文自动机可以解决。
回文自动机(PAM)
他是一个自动机,维护了所有的不同的回文串。因为一个字符串的本质不同的回文子串最多只有
(
并且一些点与点中间连了一些转移边(有向边),代表在某个回文串的两端添加字母
而且,也像其它自动机一样,每一个节点都存在一个回跳边(即 nxt 或 fail 边)。这些回跳边表示如果跑自动机到某一个节点,没有一条边匹配,就要跳最长回文 border 然后继续试配。
(这里定义最长回文 border 为一个字符串的最长为回文串的公共前后缀)
如何构造
一个 PAM 由一个回文 Trie 和一棵 Fail 树组成。每一个点要维护这个点表示的回文串的长度。设其为
而因为回文有两种情况:回文中心为字母,即长度为奇数的串;回文中心为空,即长度为偶数的串。所以我们要设两个根。
一个根编号 0,令其
1 | int tr[N][26], fail[N], len[N], cnt[N]; |
然后开始插入一个字母。我们定一个指针
我们先定一个函数
举个例子,假设现在的字符串为 aaaxxxcccxabacaba
,目前以最后一个字符结尾的最长回文串为 abacaba
。现在新插入一个字符 c
,那么首先,不存在给字符串 abacaba
同时加上 c
的字符串,那么跳 Fail。然后最长回文 border 就是 aba
。然后存在给 aba
同时加上 c
的字符串,为 cabac
。那么就返回字符串 aba
代表的节点。若跳到 0 了还没有,没事,再跳一层就是 1,即 c
,那么操作后的字符串就是 c
而不是 cc
了。
1 | int getfail(int x, int pos){ |
现在插入一个字母
若存在
否则就要新建节点。步骤如下:
首先建边。将
赋值为一个新 idx。 然后求出新节点的 Fail。因为要与这个节点不同,所以就要求
。然后设新节点的 Fail 为 即可。 - 最后修改
为 。然后设 为这个新 idx 即可。
1 | void insert(int alp, int pos){ |
一个 PAM 就造好了!可以根据个人喜好封装。
例题
还是前言的那一道。首先插入这个字母。
查询以该位置结尾的回文子串个数就可以,再维护一个
P4762 Virus synthesis IN Lv.15
题目大意
初始有一个空串,利用下面的操作构造给定串
1、串开头或末尾加一个字符
2、串开头或末尾加一个该串的逆串
求最小化操作数,
解答
首先,根据贪心,我们肯定要尽量的多使用操作 2。
那么,首先就给他建一个回文自动机。然后我们令
然后因为一个字符串
然后,从点
- Title: 回文自动机
- Author: 11490DX
- Created at : 2025-03-17 17:28:02
- Updated at : 2025-03-17 21:22:53
- Link: https://11490dx.net/2025/03/17/pam/
- License: This work is licensed under CC BY-NC-SA 4.0.