1. 程式人生 > >一篇並不對勁的後綴自動機教程

一篇並不對勁的後綴自動機教程

自己 公共子串 element 出現 src memcpy hab closed 轉移

說是教程其實也就我自己看怕我這個shabi又雙叒叕忘了後綴自動機然後再學一遍

1.後綴自動機就是能識別字符串S所有後綴的自動機

根據定義知道 它也可以識別S的所有子串

2.Right集合

是指子串str在母串S中出現的結束位置的集合

對於一個Right集合,適合它的子串長度取值區間為$[minlen(s),maxlen(s)]$

3.parent樹

根據上面Right集合的定義,我們可以按Right集合的包含關系建一棵樹,就是parent樹

父親的Right集合包含所有兒子的Right集合

很容易知道parent樹從上到下子串長度邊長,Right集合變小

我們令fa = parent(s)則可發現

$Right(s)?Right(fa)$且$Right(fa)$最小

發現$maxlen(fa)=minlen(s)?1$

parent樹可以相當於fail樹,一個單詞匹配不上的時候可以沿著parent樹往上跳

4.maxlen

根據Right集合的定義,一個點的maxlen其實就是轉移圖上root到x的最長路徑長度

順便,minlen(x)是最短的

5.其他可用的性質

I.把maxlen基數排序,就得到了轉移圖的拓撲序

II.在SAM上兩個串的最長公共子串就是這兩個點的LCA

III.由於SAM的存在,我們可以把序列的東西強行上樹,然後把樹的東西強行上字符串,所以如果您做到SAM+樹鏈剖分/SAM+LCT的時候,不要過早罵人

技術分享圖片
struct SAM
{
int tr[maxn][26],fa[maxn],len[maxn],size[maxn];
    int ST[maxn][21],id[maxn],Cnt[maxn];
    int last,p,np,q,nq,cnt,rt;
    SAM(){last = ++cnt; rt = 1;}
    inline void extend(int c)
    {
        p = last, np = last = ++cnt, len[np] = len[p] + 1;size[np] = 1;
        while(!tr[p][c] && p) tr[p][c] = np, p = fa[p];
        
if(!p) fa[np] = rt; else { q = tr[p][c]; if(len[q] == len[p] + 1) fa[np] = q; else { nq = ++cnt; len[nq] = len[p] + 1; memcpy(tr[nq],tr[q],sizeof(tr[q])); fa[nq] = fa[q]; fa[q] = fa[np] = nq; while(tr[p][c] == q) tr[p][c] = nq,p = fa[p]; } } } inline void buildsize() { for(int i=1;i<=cnt;i++)Cnt[len[i]]++; for(int i=1;i<=n;i++)Cnt[i] += Cnt[i-1]; for(int i=1;i<=cnt;i++)id[Cnt[len[i]]--] = i; for(int i=cnt;i>=1;i--)size[fa[id[i]]] += size[id[i]]; } }sam;
代碼

一篇並不對勁的後綴自動機教程