AC自动机

某个课程的作业,促使我来看看这玩意。

整个程序的算法思想是看别人的ACM的blog看懂的,感觉确实和KMP很像。但是代码呢就比较工程化一点。顺便回忆了一把ACM的感觉。

基本原理呢基于字典树,并增加了失败节点。

实现原理类似KMP算法,但是一次可以匹配多个字符串。在匹配失败时转向失败节点,并从失败节点开始继续向下匹配。

比如:我们有字典集合

acd、aceb、bef、cef

节点关系如图所示,红色为失败指针

digraph "ac_automation" {
    node [shape=box, fontsize = 14, labelfontsize = 14];
    edge [fontsize = 14, labelfontsize = 14];

    char_0 [label="0"];
    char_1 [label="1"];
    char_2 [label="2"];
    char_3 [label="acd"];
    char_4 [label="4"];
    char_5 [label="aceb"];
    char_6 [label="6"];
    char_7 [label="7"];
    char_8 [label="bef"];
    char_9 [label="9"];
    char_10 [label="10"];
    char_11 [label="cef"];

    char_0 -> char_1 [style=bold,label="a"];
    char_0 -> char_6 [style=bold,label="b"];
    char_0 -> char_9 [style=bold,label="c"];
    char_1 -> char_2 [style=bold,label="c"];
    char_2 -> char_9 [color=red];
    char_2 -> char_3 [style=bold,label="d"];
    char_2 -> char_4 [style=bold,label="e"];
    char_3 -> char_9 [color=red];
    char_4 -> char_10 [color=red];
    char_4 -> char_5 [style=bold,label="b"];
    char_5 -> char_10 [color=red];
    char_6 -> char_7 [style=bold,label="e"];
    char_7 -> char_8 [style=bold,label="f"];
    char_9 -> char_10 [style=bold,label="e"];
    char_10 -> char_11 [style=bold,label="f"];
}

当查找acefcab时,首先会按aceb的支路一直匹配到e,在e的位置发现找不到f,然后跳转到e的失败节点(即cef支路的e节点),查到f。并以此完成了第一次匹配。

接下来从根节点重新匹配并分别进入第一层的c节点,回到根节点,进入a节点,回到根节点,和进入b节点。

并在最终只匹配成功了cef

代码如下:

如注释所言,4.7.0 以前的GCC 就不用争扎了,编译不过的

以下内容包含了完整对AC自动机的解释构建过程

Last updated

Was this helpful?