迴文自動機學習筆記
阿新 • • 發佈:2021-08-12
迴文自動機
,那麼 \(len(0)=0,fail(0)=1,len(1)=-1\)。
前言
考試遇到了新科技,所以不得不學一下。
根據各種自動機的姿勢,PAM 是處理迴文問題的有力工具。
OI-Wiki 講的是好的,推薦學習。
PAM 的實用性在於,它用至多 \(O(n)\) 個節點儲存了所有的迴文串,Manacher 與之相比則有很大侷限性。
構樹
Manacher 利用插入 #
的方法避免的迴文串長度奇偶性的討論,因為這個討論真的很麻煩。
而 PAM 的方法為,建立兩個根,分別為奇根和偶根。
每個節點承載的資訊,基本的 PAM 需要記錄 \(len(i),fail(i)\) 分別表示節點 \(i\) 代表的迴文字尾的長度和失配指標。
假設奇根的下標為 \(1\),偶根的下標為 \(0\)
而奇根不需要 \(fail\) 指標,因為每個字元一定能和自己形成長度為 \(1\) 的迴文串。
每個 \(fail\) 指標實際指向的是自己的最長迴文字尾,這個和大部分自動機是一樣的。
考慮每次加入一個字元,都利用上一次的插入位置,跳 \(fail\) 來匹配迴文串。
考慮在兩頭加字元,這時候奇根的 \(-1\) 就有很好的優勢了,程式碼:
int Node(int l){ tot ++; memset(ch[tot], 0, sizeof(ch[tot])); len[tot] = l; fail[tot] = 0; return tot; } void Build(){ tot = - 1; n = las = 0; Node(0), Node(-1); fail[0] = 1; } int Get_Fail(int x){ while(str[n - len[x] - 1] != str[n]) x = fail[x]; return x; } void Insert(char c){ str[++ n] = c; int now = Get_Fail(las); if(!ch[now][c - 'a']){ int x = Node(len[now] + 2); fail[x] = ch[Get_Fail(fail[now])][c - 'a']; ch[now][c - 'a'] = x; } las = ch[now][c - 'a']; }
性質&應用
可以證明一個字串本質不同的迴文串個數至多為 \(O(|S|)\) 個,所以迴文樹節點個數是 \(O(n)\) 的。
如果需要統計一個字串的本質不同迴文子串個數,那麼就是自動機的狀態數。
而且可以在自動機上 DP 或利用它 DAG 的本質維護些奇奇怪怪的東西,反正都是線性的。
啥你需要模板題。
直接從最長字尾開始沿著 \(fail\) 指標遍歷即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N = 3e5 + 10; int n, tot, las, ch[N][30], len[N], cnt[N], fail[N]; char str[N]; int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar(); return x * f; } int Node(int l){ tot ++; memset(ch[tot], 0, sizeof(ch[tot])); len[tot] = l; cnt[tot] = fail[tot] = 0; return tot; } void Build(){ tot = - 1; n = las = 0; Node(0), Node(-1); fail[0] = 1; } int Get_Fail(int x){ while(str[n - len[x] - 1] != str[n]) x = fail[x]; return x; } void Insert(char c){ str[++ n] = c; int now = Get_Fail(las); if(!ch[now][c - 'a']){ int x = Node(len[now] + 2); fail[x] = ch[Get_Fail(fail[now])][c - 'a']; ch[now][c - 'a'] = x; } las = ch[now][c - 'a']; cnt[las] ++; } int main(){ scanf("%s", str + 1); Build(); int t = strlen(str + 1); for(int i = 1; i <= t; i ++) Insert(str[i]); LL ans = 0; for(int i = tot; i >= 2; i --){ if(fail[i] > 1) cnt[fail[i]] += cnt[i]; ans = max(ans, 1LL * cnt[i] * len[i]); } printf("%lld\n", ans); return 0; }