回文自动机学习笔记
由两棵子树构成
- 一棵节点编号为0,子树是长度为偶数的回文串
- 一棵节点编号为1,子树是长度为奇数的回文串
回文自动机
奇偶回文串处理
使得在号子树下的字符串长度
使得字符串最终能跳到,即本身为回文串
使得本身为回文串的情况,最小真后缀回文串为0成立
回文树节点
- 对于号子树,路径上的字母表示回文串后半段
- 以号节点为例,表示的是
baab
- 对于号子树,路径上的字母表示回文串后半段(包括中间字符)
- 以号节点为例,表示的是
此图片为的回文树
建树
,为必定不相同的字符
:表示当前回文串的最长真后缀回文串
- 找到最新节点,
- 寻找代表回文串的前一个点:
- 与当前节点()比较,若相当于在原回文串两边加上一个字符
ch
- 创建新的节点且
- 若当前回文串不匹配,跳指针寻找最长真后缀回文子串是否匹配
- 最终至少会跳到,字符本身必定为回文串
指针建立
寻找一个回文串的最长真后缀回文子串
相当于寻找回文串的最长真后缀回文子串的匹配回文串
- 定义一个新变量跳到
- 如建树一样寻找匹配回文串即可
- 对于以自己为回文串的回文串,不然至少找到本身
代码
:字符串
:字典树
:指针跳转到当前回文串的** 最长真后缀回文串**
:当前回文串的长度
初始化
scanf("%s", in + 1); in[0] = '#';
//第一个为不同字符
fail[0] = 1; fail[1] = 0;
//保证最终跳到本身
len[0] = 0; len[1] = -1;
//易处理处理
last = 0;
//last最新节点置0
num = 1;
//字典树编号,已有2个节点:0,1
建树
for (int i = 1; i <= n; i++) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
//跳到第一个匹配的串
if (!tree[last][in[i] - 'a']) {//若改字符串不存在
len[++num] = len[last] + 2;//新的回文串为原回文串+2
int j = fail[last];//当前串的最长真后缀回文串
while (in[i - len[j] - 1] != in[i]) j = fail[j];
//最长真后缀回文串的第一个匹配串
fail[num] = tree[j][in[i] - 'a'];
tree[last][in[i] - 'a'] = num;
//字典树编号
}
last = tree[last][in[i] - 'a'];
//更新last
}
模板题
P5496 回文自动机(PAM)
P4287 双倍回文
正反向建两个回文自动机