一篇并不对劲的后缀自动机教程

说是教程其实也就我自己看怕我这个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;
代码

 

posted @ 2018-06-17 22:14  探险家Mr.H  阅读(180)  评论(0编辑  收藏  举报