后缀自动机

Posted lhm-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀自动机相关的知识,希望对你有一定的参考价值。

后缀自动机可以理解为是将字符串所有后缀所建出的(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 o aab)

技术图片

( aabb o aabba)

技术图片

( aab o 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);

以上是关于后缀自动机的主要内容,如果未能解决你的问题,请参考以下文章

JSP是什么?

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

Sublime Text3自定义代码片段

后缀自动机多图详解(代码实现)