迴文自動機(PAM)
阿新 • • 發佈:2021-12-24
迴文自動機(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\)構成的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; }