1. 程式人生 > >淺談後綴自動機SAM

淺談後綴自動機SAM

spa 有一個 大神 次數 cpp 模板 ring 子節點 body

一下是蒟蒻的個人想法,並不很嚴謹,僅供參考,如有缺誤,敬請提出

參考資料:
陳立傑原版課件
litble
某大神
某大神
其實課件講得最詳實了

有限狀態自動機

我們要學後綴自動機,我們先來了解一下自動機到底是什麽。【雖說以前也學過AC自動機,只是當一個名字罷了】

有限自動機的功能是識別字符串,作用各不相同
如果自動機A能識別串s,那麽A(s) = true
自動機有一個初始狀態,從初始狀態出發能到達多個狀態。到達終止狀態表示字符串識別

後綴自動機SAM

我們略去建機原理的分析和建機過程,具體原理建議看陳立傑神牛的課件,建機過程為了簡化可以看litble的

一些性質:
①後綴自動機能識別對應串的所有後綴,且狀態數最少【最簡狀態】
②從初始狀態出發,每一種走法唯一對應一種子串
【也就是說一個節點往後有幾種走法,往後就有幾種子串】
③一個狀態代表一個子串集合,該集合中的子串有著相同的右端點,且長度連續
④一個狀態的pre指針指向的狀態與該狀態也有著相同的右端點,且長度最大值 = 該狀態最小長度 - 1

由此可見pre是當前串的後綴

⑤一個狀態表示子串的最大長度Max(u) = step[u],最小長度Min(u) = step[pre[u]] + 1【由④得】
⑥如果不同位置的相同子串需重復計算,則一個點表示子串的數量 = 其parent樹中的葉子個數
⑦只有葉子節點表示的子串是不重復的
⑧後綴自動機是拓撲圖,pre指針形成一棵樹
⑨插入時第一個建的點都是主鏈上的點
⑩求點的拓撲序可以用step進行基數排序

一些作用:【大多與子串相關】
①求第K小子串
②求LCP【最長公共子串】
③求子串出現次數,最大次數等
④求某個位置為結尾最大匹配長度
⑤求不同子串數
還有很多。。。。。
蒟蒻見過的差不多這些

溜了溜了。。。
貼個模板

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k; k = ed[k].nxt)
using namespace std;
const int maxn = 2000005,maxm = 100005,INF = 1000000000
; inline int RD(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - ‘0‘; c = getchar();} return out * flag; } int ch[maxn][26],pre[maxn],step[maxn],n,cnt,last; int b[maxn],sz[maxn],a[maxn]; LL ans = 0; char s[maxn]; void ins(int u){ int p = last,np = ++cnt; last = np; step[np] = step[p] + 1; while (p && !ch[p][u]) ch[p][u] = np,p = pre[p]; if (!p) pre[np] = 1; else { int q = ch[p][u]; if (step[q] == step[p] + 1) pre[np] = q; else { int nq = ++cnt; step[nq] = step[p] + 1; for (int i = 0; i < 26; i++) ch[nq][i] = ch[q][i]; pre[nq] = pre[q]; pre[q] = pre[np] = nq; while (ch[p][u] == q) ch[p][u] = nq,p = pre[p]; } } sz[np] = 1; } void solve(){ REP(i,cnt) b[step[i]]++; REP(i,cnt) b[i] += b[i - 1]; REP(i,cnt) a[b[step[i]]--] = i; for (int i = cnt; i; i--){ sz[pre[a[i]]] += sz[a[i]]; if (sz[a[i]] > 1) ans = max(ans,1ll * step[a[i]] * sz[a[i]]); } } int main(){ scanf("%s",s + 1); cnt = last = 1; n = strlen(s + 1); for (int i = 1; i <= n; i++) ins(s[i] - ‘a‘); solve(); printf("%lld",ans); return 0; }

淺談後綴自動機SAM