淺談後綴自動機SAM
一下是蒟蒻的個人想法,並不很嚴謹,僅供參考,如有缺誤,敬請提出
參考資料:
陳立傑原版課件
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