回文自动机(PAM)
回文自动机(PAM)
常用于解决回文串相关的计数问题(可以类比后缀自动机,不过比后缀自动机简单多了)
PAM支持动态插入一个字符,建一课PAM的过程可以看做一个一个字符插入的过程
PAM的建立:
PAM的每个节点代表了一个回文子串,并且PAM每个节点上有四个属性:
\[len[u]:节点u所代表回文子串的长度\\
cnt[u]:节点u所代表回文子串的出现次数\\
ch[u][tp]:节点u在所代表的回文子串在前后各加上一个字符'tp'所对应的下一个字符串\\
fail[u]:节点u所代表的回文子串的一个最长回文后缀所对应的节点\\
\]
初始PAM上有两个点:
\[0:len = 0表示偶数长度的回文子串\\
1:len = (-1)表示奇数长度的回文子串\\
\]
记上一个插入的位置为\(last节点\),初始\(last = 0 , fail[0] = 1\)
并且显然有,\(last\)构成的fail树是以当前已经插入的字符串为结尾的所有回文子串所对应的节点\((1)\)
下面只考虑插入状态为\((last)\)的平凡情况
类比AC自动机一样,对于当前插入的字符串\(c\),在last对应的\(fail树\)上找一个可以匹配的节点
记当前遍历fail树上的节点\(v\) , 节点\(v\)掌控的区间实际上是\([|S| - 1 - len[v] + 1 , |S| - 1]\)
根据上面(1),很显然可以直接遍历一次fail树就可以处理出当前的fail区间
找到后,指一下儿子过来,处理一下fail就好了
\((lgP3649 [APIO2014]回文串)\)代码:
#include<bits/stdc++.h>
#define MAXN 300005
typedef long long ll;
using namespace std;
int n;
int a[MAXN],last;
char s[MAXN];
int ch[MAXN][29],fail[MAXN],cnt[MAXN];
int tot = (-1),len[MAXN];
ll ans;
int newnode(int x){
	tot++;
	len[tot] = x;
	return tot;
}
int getfail(int x , int y){
	while(a[n - len[x] - 1] != a[y]){
		x = fail[x];
	}
	return x;
}
int main(){
	scanf("%s" , s + 1);
	newnode(0) , newnode(-1);
	fail[0] = 1 , a[0] = (-1);
	int cur,now;
	for(n = 1 ; s[n] ; n++){
		a[n] = s[n] - 'a' + 1;
		cur = getfail(last , n);
		if(!ch[cur][a[n]]){
			now = newnode(len[cur] + 2);
			fail[now] = ch[getfail(fail[cur] , n)][a[n]];
			ch[cur][a[n]] = now;
		}
		last = ch[cur][a[n]];
		cnt[last]++;
	}
	for(int i = tot ; i > 1 ; i--){
		if(fail[i] > 1)cnt[fail[i]] += cnt[i];
		ans = max(ans , 1ll * cnt[i] * len[i]);
	}
	cout<<ans<<endl;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号