• 后缀自动机构建图解


    后缀自动机构建图解

    我是在这学的:https://www.luogu.com.cn/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie

    感觉作者画的图有点难理解,而且讲到了所以来画一画,方便复习。看本文前最好先把上面那个看一遍!

    此处是从右往左增量,画图时没注意

    代码:(注意case的位置)

    struct NODE
    {
        int ch[26];
        int len,fa;
        NODE(){memset(ch,0,sizeof(ch));len=0;}
    }dian[MAXN<<1];
    int las=1,tot=1;
    void add(int c)
    {
        int p=las;int np=las=++tot;
        dian[np].len=dian[p].len+1;
        for(;p&&!dian[p].ch[c];p=dian[p].fa)dian[p].ch[c]=np;
        if(!p)dian[np].fa=1;//以上为case 1
        else
        {
            int q=dian[p].ch[c];
            if(dian[q].len==dian[p].len+1)dian[np].fa=q;//以上为case 2
            else
            {
                int nq=++tot;dian[nq]=dian[q];
                dian[nq].len=dian[p].len+1;
                dian[q].fa=dian[np].fa=nq; 
                for(;p&&dian[p].ch[c]==q;p=dian[p].fa)dian[p].ch[c]=nq;//以上为case 3
            }
        }
    }
    char s[MAXN];int len;
    int main()
    {
        scanf("%s",s);len=strlen(s);
        for(int i=0;i<len;i++)add(s[i]-'a');
    }
    

    case1

    首先处理好parent树信息:

    int p=las;int np=las=++tot;
    dian[np].len=dian[p].len+1;
    

    case1就是直接接到根上,下面为了方便理解把所有压缩的点都画出来了

    实现方法(参照代码):

    for(;p&&!dian[p].ch[c];p=dian[p].fa)dian[p].ch[c]=np;
        if(!p)dian[np].fa=1;//以上为case 1
    
    1. 新加进来的字符变量名为 (c)
    2. 新建点 (np(11))
    3. 查看前面的点有没有能接上的,即有c这条出边
    4. 这种情况为到根都没有,直接接上根点(1)

    case2

    前面有这个出边而且长度等于新后缀减一,此时直接连到后面。

    这种情况很简单,因为只能是前一个后缀长度才能相差一

    实现方法:

    int q=dian[p].ch[c];
    if(dian[q].len==dian[p].len+1)dian[np].fa=q;//以上为case 2
    
    1. (q(2)) 为前面的有出边为 (c) 字符(此处 (c) 为'a')的点
    2. 长度符合,能接上,把(np(3))直接接到(q(2))

    case3

    回到case1的图,此时后缀自动机上真实存在的点只是红圈的点

    未命名.png

    现在加入 (acabd),发现有根有 (a) 的出边,但是不能直接接上,因为实际上不存在5这个点。

    也可以理解为5是被压缩的点。

    于是需要把 (abd) 这个等价类分成 (a)(abd) 两个点 ,然后把 (acabd) 接到 (a) 上:

    未命名.png

    如图,a现在也变成了红圈点,这就是新建点的过程。

    实现方法:

     else{
          int nq=++tot;dian[nq]=dian[q];
          dian[nq].len=dian[p].len+1;
          dian[q].fa=dian[np].fa=nq; 
          for(;p&&dian[p].ch[c]==q;p=dian[p].fa)dian[p].ch[c]=nq;//以上为case 3
     }
    
    1. 新建点 (nq(5))
    2. (q(7)) 的信息传给 (nq(5)) :包括转移,parent树上父亲,等价类最大长度
    3. (nq(5)) 最大长度此时为 (len(p(1))+1=2),因为加了新增的字符 (a)
    4. (q(7))(np(15)) 的父亲置为 (nq)
    5. 根据转移边要尽量近的原则,把原先a转移为 (q(7)) 的转移边连到 (nq(5)) 上(从 (p(1)) 开始)

    做完了!やったぜ ~

    感觉第一次真正理解了后缀自动机,图有点丑见谅啦

  • 相关阅读:
    Code first常用命令
    vue问题集锦
    net WebConfig读写
    CefSharp使用心得
    工作问题
    工作总结
    HttpContext.Current.Session为null
    Java基础00-接口组成更新31
    Java基础00-Lamda表达式30
    Java基础00-网络编程29
  • 原文地址:https://www.cnblogs.com/lcyfrog/p/12259653.html
Copyright © 2020-2023  润新知