回文自动机(PAM)

https://www.luogu.com.cn/problem/P5496

https://www.luogu.com.cn/blog/luotianze/solution-p5496

#include <bits/stdc++.h>
using namespace std;
const int N=(int)(2e6+5);
char s[N];
int n,fail[N],len[N],ans[N],ch[26][N],cur,tot=1;

int get_fail(int x,int i) {
	while(i-len[x]-1<0||s[i-len[x]-1]!=s[i]) x=fail[x]; 
	// x-len[x]-1 <0 : 跳到偶根
	return x; 
}

signed main() {
	scanf("%s",s); n=strlen(s);
	fail[0]=1; len[1]=-1; int las=0;
	for(int i=0;i<n;i++) {
		if(i>=1) s[i]=(s[i]-97+las)%26+97;
		int p=get_fail(cur,i),c=s[i]-'a';
		if(!ch[c][p]) {
			++tot; 
			fail[tot]=ch[c][get_fail(fail[p],i)];
			ch[c][p]=tot;
			len[tot]=len[p]+2; 
			ans[tot]=ans[fail[tot]]+1;
		}
		cur=ch[c][p]; printf("%d ",las=ans[cur]);
	} 
}

几点做题时遇到的性质

  1. 本质相同的回文子串在回文树上的对应节点相同(1 个 1 个匹配显然)

  2. cur 维护的是 \([1,i-1]\) 的最长后缀回文子串在后缀树上对应的节点。考虑当前是不是要增加 \(s_i\) 这个字符(在自动机的构建中大多都是以增量法来构建的),那么显然我们要找到一个最小的 \(j\) 满足 \([j,i]\) 是回文串,显然 \([j+1,i-1]\) 是回文串,所以我们跳 cur 的 fail 指针,考虑能否增量这一个新字符。考虑之后 \(p\) 对应的就是 \([q,i-1]\) 最小的 q 满足 2 端都能增量新字符的 \(s[q,i-1]\) 在后缀树上的节点。那么考虑一个回文串在回文树上的匹配,是不是假如本质相同了,那么到达的也应该也一样啊?(类 Trie)也就是要看看当前是否出现过。后面的 \(fail\) 的构造就类 AC 自动机了。需要注意的是,虽然当前都是在以 \(i\) 为结尾的构造,但是本质相同的话的构造与当前的构造一定是一样的。

posted @ 2022-08-04 17:18  FxorG  阅读(47)  评论(0)    收藏  举报