zoukankan      html  css  js  c++  java
  • 【转】后缀自动机初识

    2017-11-21:

    看了一些博客,云里雾里,然后去看CLJ的ppt,更是蒙蔽了。如果直接有图的话,比划比划可能直观一点。

    发现这个还不错啦。

    http://www.cnblogs.com/oyking/archive/2013/08/02/3232872.html

     http://fanhq666.blog.163.com/blog/static/8194342620123352232937/

     

     

    举个例子就是aabbabd,以此构造后缀自动机(请耐心看,因为前面几步没有体现算法)

    有几点要记得:

     

    1.由一个接受态沿Parent往前走所到的状态也是接受态

    2.一个节点及其父辈的代表的串有相同的后缀

     

    1.首先神马都没有:

     

    此时后缀只有一个就是空串:

     

    红字表示此节点代表最长串的长度,一个节点可能代表多个串

    2.现在构建"a"的自动机,就是下面这样

     

    现在后缀变成了这样:


    3.然后以上为基础构建"aa"的自动机

    现在想一下,由S或者说0号节点可以接受的后缀为空串和"a"这两个,那么现在要将"aa"和"a"这两个后缀更新到后缀自动机中,那么1号节点的后缀"a"就要加入一个字符"a",而空串也要加入字符"a"

    也就是所有之前的后缀都要在后面加入一个字符"a".

    但是由于1号节点之前所代表的后缀"a"和1的Parent所代表的后缀(空串)+"a"代表的一样,所以,无需更新1及之前的可接受态

    如下图:


    自动机就变成了如下:

     

    3.更新自动机变成"aab"自动机

    同上加所有接受态也要调整,就是在后面加上"b"字符:


    这时,由于1,2节点无法代表三个后缀的任意一个,所以除空串的所有后缀都由3代替

    这时3号节点和0号节点为接受态.

    自动机成了这样:


    具体过程是这样的:

    S1:新建节点3

    S2:找到最后一个后缀也就是最后一个接受态是节点2

    S3:2号节点直接连向3,表示插入后缀"aab"

    S4:向上找2的Parent,1号节点,向3连边,表示插入后缀"ab"

    S5:找到S,连边,表示插入后缀"b".

    S6:没有其他接受态了,那么3的上一个接受态为S,Parent[3]=S

    4:更新成"aabb"的自动机

    同理,在所有接受态后加上字符"b"

    不过由于接受态0(S)的转移"b"已经存在,那么,由于不能破坏原来的中间态的转移,只能新建一个节点,来代替接受态0(S)的转移节点


    自动机成了这样:


    找到0(S)时,发现转移"b"已经有节点占了,所以新建节点5,将3号所有信息Copy(包括Parent),然后更新len值,就是node[5]->len=node[5]->parent->len+1,所以5号节点可以代表后缀空串(0号代表的串)+字符"b"=后缀"b",节点3成了中间态,所以将节点为原接受态的节点指向3的转移改为指向5,这时,我们发现指向3的原接受态节点一定是当前节点0(S)及当前未访问的原接受态节点,所以可以直接沿着Parent往上更新.

    然后节点5的Parent及祖先加入了现在的接受态

    再次重申一点:一个节点及其父辈的代表的串有相同的后缀,且代表串长度递减,由于5号节点是接受态,所以他的父辈也是接受态,同时反过来也一样,与任意接受态拥有相同后缀的长度小于当前节点的未访问节点一定是当前节点的父辈,如与5号节点有相同后缀的长度小于5号节点的未访问的节点一定是5号的父辈,一定可以作为接受态.

    因此为了维护这个性质,我们应该将3号节点的父亲重定义为5

    到这里基本上应该明白了

    就将剩下的构造过程放出来:

     

     

    代码什么的如下:

    插入一个节点:

     

    意义:

    当前新建节点为np,最后的接受态为tail,接受态指针p,pool是内存池,就是一个很大的数组,2*n的空间吧

    init就是0号节点

    Step 1:建立节点np,p指向tail,np的len更新

    Step 2:沿着Parent寻找第一个有转移冲突的接受态节点并沿途更新转移

    Step 3:

    如果找不到,np的Parent更新为init.

    如果正好冲突的节点长度为当前接受态节点那么np的Parent赋为冲突的节点.

    否则,新建节点r,Copy冲突节点所有信息,更新r->len=p->len+1,将冲突节点的Parent和np的Parent赋为r,再往前更新与冲突节点有关的转移.

    至此结束..........

     

    待续。。。

  • 相关阅读:
    PVS 7.6 部署教程
    PHP下载远程图片的3个方法
    [Xcode 实际操作]二、视图与手势-(2)UIView视图的层次关系
    [Swift]检查API可用性
    [Xcode 实际操作]二、视图与手势-(1)UIView视图的基本使用
    [Swift]LeetCode103. 二叉树的锯齿形层次遍历 | Binary Tree Zigzag Level Order Traversal
    [Swift]forEach详解
    [Swift]LeetCode937. 重新排列日志文件 | Reorder Log Files
    [Swift]LeetCode940. 不同的子序列 II | Distinct Subsequences II
    [Swift]LeetCode939. 最小面积矩形 | Minimum Area Rectangle
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7875576.html
Copyright © 2011-2022 走看看