一、概述
AC自动机在1975年产生于贝尔实验室,是著名的多模匹配算法之一。
之前谈到一种单模式匹配算法,KMP。与之比较,KMP是用来在一篇文章中匹配一个模式串;而假如存在多个模式串,按照KMP的思路就需要进行多轮重复匹配,所以这时候就需要一种更加有效率的方式。
AC自动机 = 字典树 + KMP
对于字典树和KMP算法不了解的,可以参考以下两篇博文:
1、字典树
2、KMP算法
二、基本原理
1、KMP和字典树
之前谈到,在KMP进行单模式匹配时,只需要线性的扫描一遍文本串,在扫描过程中出现匹配失败时,可以根据失配表,对移动位置进行确定,继续进行匹配。
而多模式的匹配需要怎么进行呢?考虑一下字典树,这是一种多模式的匹配,假如将多个模式串组合形成一个字典树,再结和KMP算法,这就是一种多模式匹配的思路了。
2、基本构造
按照上面的思路,将模式串处理称为字典树,对文本串进行匹配时,AC自动机应该具有三种状态:
(1)按字符匹配成功,继续匹配
从字典树的角度来说,就是当前节点的字符与文本串的字符相同,则继续按照字典树路径进行匹配
(2)按字符匹配成功,到达结尾
同样这里匹配成功后,但已经达到结尾,匹配结束
(3)按字符匹配失败
正常字典树匹配失败,则匹配结束。但结合了KMP算法中的失配表,AC自动机在匹配失败时,会进行路径跳转,通过失配路径进行进行匹配,直到匹配成功或者回到根节点
三、实例
以经典的ushers为例,模式串是[he、she、shr,say、her],文本串为‘ushers’
1、构建字典树
2、构造失配路径
步骤(1)节点'h'父节点为root节点,失配路径直接指向root节点
步骤(2)节点's'父节点为root节点,失配路径直接指向root节点
步骤(3)节点'e'父节点为'h',寻找节点'h'的前缀指针root节点,是否有字符为'e'的子节点,没有;
前缀指针已为root节点,失配路径指向root节点
步骤(4)节点'a'父节点为's',寻找节点's'的前缀指针root节点,是否有字符为'a'的子节点,没有;
前缀指针已为root节点,失配路径指向root节点
步骤(5)节点'h'父节点为's',寻找节点's'的前缀指针root节点,是否有字符为'h'的子节点,有;
失配路径指向该字符为'h'的子节点
步骤(6)节点'r'父节点为'e'节点,寻找节点'e'的前缀指针root节点,是否有字符为'r'的子节点,没有;
前缀指针已为root节点,失配路径指向root节点
步骤(7)节点'y'父节点为'a'节点,寻找节点'a'的前缀指针root节点,是否有字符为'y'的子节点,没有;
前缀指针已为root节点,失配路径指向root节点
步骤(8)节点'e'父节点为'h'节点,寻找节点'h'的前缀指针'h'节点,是否有字符为'e'的子节点,有;
失配路径指向该字符为'e'的子节点
步骤(9)节点'r'父节点为'h'节点,寻找节点'h'的前缀指针'h'节点,是否有字符为'r'的子节点,没有;
继续寻找前缀指针节点'h'的前缀指针root节点,是否有字符为'r'的子节点,没有;
到这里,AC自动机的整个树就构造完毕了
3、匹配过程
自动从root节点出发,进行匹配,对于文本串‘ushers’:
(1)字符u在root节点的子节点无法找到,失配路径指向root节点
(2)字符s在root节点的子节点中存在,沿着字典树路径前进,到达's'节点
(3)字符h在's'节点的子节点中存在,沿着字典树路径前进,到达'h'节点
(4)字符e在'h'节点的子节点中存在,沿着字典树路径前进,到达'e'节点,输出she字符
(5)字符r在'e'节点的子节点中无法找到,失配路径指向前缀指针'e'节点;输出he字符
字符r在前缀指针'e'节点中存在,沿着字典树路径前进,到达'r'节点,输出her字符
(6)字符s在'r'节点的子节点中无法找到,失配路径指向root节点
(7)匹配结束,输出字符为[she,he,her]
在上述步骤可以看到,算法在步骤5中,无法在子节点中找到字符r,此时会自动转向另一条路径,输出匹配成功的he及her字符;对于通过失配路径到达当前路径之后,状态机像从来没有走过之前的路径一样(从字典树上看,像直接从root节点出发到达),这样没有“失败”的从初始路径出发,直到匹配结束,体现相当高的效率。