某个课程的作业,促使我来看看这玩意。
整个程序的算法思想是看别人的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
代码如下:
/**
* AC 自动机, 数节点类和自动机功能类
* 文档格式:doxygen
* @author owentou, owt5008137@live.com
* @date 2012.08.28
*/
#ifndef __AC_AUTOMATION_HPP_
#define __AC_AUTOMATION_HPP_
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include
如注释所言,4.7.0 以前的GCC 就不用争扎了,编译不过的
以下内容包含了完整对AC自动机的解释构建过程