1. 程式人生 > 其它 >迴文自動機(PAM)

迴文自動機(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;
}