码迷,mamicode.com
首页 > 其他好文 > 详细

后缀自动机

时间:2020-01-22 21:59:23      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:技术   src   end   图片   empty   size   aaa   pre   continue   

后缀自动机可以理解为是将字符串所有后缀所建出的\(Trie\)进行压缩后得出的\(DAG\)

对于一个子串\(s\),它结束位置的集合称为\(endpos(s)\),如对于\(aaabbaab\)\(endpos(ab)={4,8},endpos(bb)={5}\)

\(s_1,s_2\)为原串的两个子串,设\(|s_1| \leqslant |s_2|\),则\(s_1\)\(s_2\)的后缀当且仅当\(endpos(s_2) \subseteq endpos(s_1)\)\(s_1\)不是\(s_2\)的后缀当且仅当\(endpos(s_1) \cap endpos(s_2)= \varnothing\)

如果两个子串的\(endpos\)相同,则其中子串一个为另一个的后缀

\(endpos\)的包含关系可以得出一个树形结构,称为\(Parent\)

技术图片

后缀自动机的节点就是\(Parent\)树的节点,每个节点表示一个\(endpos\)

\(SAM\)的节点数不超过\(2n-1\),边数不超过\(3n-4\),数组大小应开成两倍

\(len:\)为一个\(endpos\)所对应的子串中最长子串的长度

\(ch:\)为转移函数

\(fa:\)为后缀连接

\(minlen\)为一个\(endpos\)所对应的子串中最短子串的长度,得

\(len[fa[i]]+1=minlen[i]\)

后缀自动机是一张有向无环图,其中顶点是状态,而边代表了状态之间的转移

每一个状态包含了它包含的最长子串的一些连续长度的后缀,不是所有后缀,再短的其他后缀在\(fa\)连接的状态,也就是该串的所有后缀在\(Parent\)树的链上

从初始状态经由任意路径走到某一终止状态,得到的字符串为原串的某一后缀

从初始状态经由任意路径走到某一状态,得到的字符串为原串的某一子串

所有终止状态包含了原串的所有后缀,所有终止状态又用\(fa\)相连

\(abbb\)

技术图片

构造\(SAM\)时有三种情况

\(\ aa\ \to \ aab\)

技术图片

\(\ aabb\ \to \ aabba\)

技术图片

\(\ aab\ \to \ aabb\)

技术图片

\(code:\)

void init()
{
    las=tot=1;
}
void insert(int c)
{
    int p=las,np=las=++tot;
    len[np]=len[p]+1;
    while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
    if(!p)
    {
        fa[np]=1;
        return;
    }
    int q=ch[p][c];
    if(len[q]==len[p]+1) fa[np]=q;
    else
    {
        int nq=++tot;
        len[nq]=len[p]+1;
        fa[nq]=fa[q],fa[np]=fa[q]=nq;
        memcpy(ch[nq],ch[q],sizeof(ch[q]));
        while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
    }
}

一个状态包含的子串的出现次数可以先拓扑,再用\(fa\)求出,\(siz\)初值为\(1\)\(siz[1]\)应为\(0\)

\(code:\)

void calc()
{
    for(int i=1;i<=tot;++i) b[len[i]]++;
    for(int i=1;i<=tot;++i) b[i]+=b[i-1];
    for(int i=1;i<=tot;++i) ord[b[len[i]]--]=i;
    for(int i=tot;i;--i)
    {
        int p=ord[i];
        siz[fa[p]]+=siz[p];
    }
}

广义后缀自动机,在\(trie\)上建\(SAM\)

\(code:\)

void init()
{
    las=tot=pre[0]=1;
}
void insert(int c)
{
    int p=las,np=0;
    if(!ch[p][c])
    {
        np=las=++tot;
        len[np]=len[p]+1;
    }
    while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
    if(!p)
    {
        fa[np]=1;
        return;
    }
    int q=ch[p][c];
    if(len[q]==len[p]+1)
    {
        if(np) fa[np]=q;
        else las=q;
    }
    else
    {
        int nq=++tot;
        if(!np) las=nq;
        len[nq]=len[p]+1;
        fa[nq]=fa[q],fa[q]=nq;
        if(np) fa[np]=nq;
        memcpy(ch[nq],ch[q],sizeof(ch[q]));
        while(p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
    }
}
void bfs(int s)
{
    queue<int> q;
    q.push(s);
    fath[s]=0;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        las=pre[fath[x]];
        insert(co[x]);
        pre[x]=las;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(y==fath[x]) continue;
            fath[y]=x;
            q.push(y);
        }
    }
}

......

for(int i=1;i<=n;++i)
    if(d[i]==1)
        bfs(i);

后缀自动机

标签:技术   src   end   图片   empty   size   aaa   pre   continue   

原文地址:https://www.cnblogs.com/lhm-/p/12229578.html

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!