后缀自动机

爱⌒轻易说出口 提交于 2019-11-26 17:00:02

后缀自动机是一个很复杂的算法,下面讲一讲建图和应用。

建图

首先要明确后缀自动机上点和含义:一个点代表的是一类有相同后缀的字符串,而每一个点的fail一定是它本身的后缀。

那么我们定义如下结构体:

struct point
{
    int len;
    int fa;
    int ch[30];
};
point a[MAXN];

其中fa就是fail,ch表示点的出边(a~z),len就表示这个点能表示的最大后缀长度。

 

然后对于一个字符串,我们每次加入一个点。

设当前点为np,之前的主干上的最后一个点为p,当前加入的字符为c。

显然,a[np].len=a[p].len+1(因为从a[p].len出来加上一个c字符就到了np)。

接着我们就要求fail,这一个要分类讨论。

我们从np开始跳fail链,要保证跳到的点的没有c出边,对于这些点,我们把它的c出边设为np。

然后如果直接跳到了1都还没有发现一个有c出边的点,那么就把a[np].fail设为1(这个是case1)。

如果找到了一个有c出边的点,设它的出边连向q,那么就判断一下a[q].len==a[p].len+1(注意此时p是那个有c出边的点)。

如果相等,那么a[np].fa=q,这一个是没问题的了(case2)。

否则我们还要新建一个nq,使a[nq].len=a[p].len+1,然后把q的出边信息全部赋给nq。接下来,a[nq].fa=p,a[q].fa=nq。最后把p的fail链上的c出边连向q的点的c出边连向nq。

这么做是因为q的后缀未必就是np的后缀(因为a[q].len!=a[p].len+1,即可能加上很多个字符),所以我们不能直接把a[np].fail赋为q,我们需要新建一个nq节点。

以上就是建图了,时间复杂度是O(n)级别的。

贴一下代码:

int insert(int c)
{
    int i,j,p,np,q,nq;
    p=last;
    ne++;np=ne;
    a[np].len=a[p].len+1;
    for(;p>=0&&a[p].ch[c]==0;p=a[p].fa)a[p].ch[c]=np;
    if(p==0)a[np].fa=1;
    else
    {
        q=a[p].ch[c];
        if(a[q].len==a[p].len+1)a[np].fa=q;
        else
        {
            ne++;nq=ne;
            a[nq]=a[q];
            a[nq].len=a[p].len+1;
            a[q].fa=nq;a[np].fa=nq;
            for(;a[p].ch[c]==q;p=a[p].fa)a[p].ch[c]=nq; 
        }
    }
    last=np;
}

 

应用

首先我们要知道一个性质:从1点走出的任意一条路径都是原串的子串。

然后有一下几个常见的问题:

  • 判断一个串是否为原串的子串

直接从1开始走即可

  • 求一共有多少个子串

这是一个范围很广的问题,它要分相同的子串是否算同一个。

首先我们设f[i]表示i点代表的字符串有几个。若相同的子串算同一个,那么f[i]=1显然。否则我们就要花点时间求一求f[i]了。

求f[i]时,我们发现对于每一个包含前缀的点(即每一次的np),它的fail链(即它的所有后缀)上的字符串的出现次数都要加1.

这样就可以了。具体操作就是f[np]=1,然后用差分的方法把f都加上(即f[a[x].fail]+=f[x])。

至于为什么这样做是对的,我们可以想一下,每一个np就是一个前缀,而它的fail链就是前缀的后缀,所以这样就可以覆盖所有的子串了。

首先我们设size[i]表示从i点开始往下走可以走出多少条路径。那么size[x]=sum(size[y])+f[x](y为x可到达的点)。这个方程的含义就是x可以走向任何一个y可以走到的字符串,并且不走的话它本身还有f[x]个字符串。

求出了size之后,很多问题(例如查排名、求个数)都可以解决了。

 

注意:

  • 在查排名的时候每到一个z,k就要减去f[z]。因为就算我接下来不往下走,我也会得到f[z]的字符串。
  • 求f是根据fail来dfs,求size是根据出边来dfs。
  • 后缀自动机的空间一般开2n。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!