1. ac自动机简介
ac自动机算法全称Aho–Corasick算法,它是一种经典的高效字符串匹配算法,他所针对的核心问题为:
如何从一个长字符串中抽取出所有位于目标字典中的词汇。
放到实际问题当中,一个典型的例子就是:
在一串长文本当中,考察其中是否又出现敏感词并对其进行抽取。
一种非常暴力的做法就是对关键词词表中的每个词进行查找,但是这在词表非常大的情况下就会直接炸裂掉,因为他的时间复杂度将会是 O ( l × n × m ) O(l \times n \times m) O(l×n×m)这个量级,其中, l l l为字符串长度, n n n为关键词词表中的词汇数目, m m m为词表中最长的单词长度。
更好地方式是采用trie树,这样可以直接将时间复杂度降至 O ( l × m ) O(l \times m) O(l×m)。
有关trie树的原理和实现可以参考我的另一篇文章:Python笔记:Trie树结构简介,里面大致讲了一下trie树的原理,并给了一些leetcode上面的例题说明。
事实上,trie树基本上也就是ac自动机的核心原理了,不过ac自动机的原理更进了一步,他在trie树的基础上引入了失配指针,从而进一步提升了匹配的效率,将时间复杂度降至 O ( l ) O(l) O(l)量级。
更为具体的原理我们在下一个小节中尝试进行说明。
2. ac自动机原理
如前所述,ac自动机核心原理包括两点:
- trie树;
- 失配指针;
其中,trie树没什么好多说的,可以参考之前的博客:Python笔记:Trie树结构简介,这里就不再赘述了。
重点想说一下失配指针,它的核心功能类似于KMP字符串匹配算法,即当匹配失败时,直接跳转到下一个最长匹配子串。例如,假设一个子串为abcdefk
,然后关键词词典中同时有abcdefg
和cdefk
,那么,当匹配到k
时,abcdefk
和abcdefg
匹配失败,正常的情况应该是退回到b
重新进行匹配,但是,使用了失配指针之后跳转到cdef
路径下考察下一个字符串是否是k
。
不过有关这部分内容的具体实现,我倒是没有想到什么很好的办法,但是下面参考链接中的第一篇文章给了一种基于bfs的算法实现,有兴趣的读者可以参考一下。
btw,参考链接中的第一篇文章是公司里面一个大佬写的,原理上个人觉得讲的真心不错,有兴趣的读者可以细读一下。
3. ac自动机实现
最后,我们来看一下ac自动机在实际使用中的使用方法。
这里,我们主要介绍一下基于pyahocorasick库的ac自动机实现方法。
其安装方式也十分简单,只需要简单地使用下述命令pip安装一下就行了。
pip install pyahocorasick
1. ac自动机的构建
要使用ac自动机进行字符串匹配,我们首先要根据关键词词典构建一个ac自动机。
给出示例代码如下:
keywords = ["abc", "abed", "bcdf", "bcde", "aabe"]
import ahocorasick
# 创建并往 AC 树添加内容
ac = ahocorasick.Automaton()
for w in keywords:
ac.add_word(w, w)
ac.make_automaton()
2. ac自动机的调用
构建了上述ac自动机之后,我们即可使用iter函数进行string中的关键词匹配。
给出调用代码如下:
s = "abedefaabcdf"
for idx, w in ac.iter(s):
print("{} :: s[{}:{}]".format(w, idx, idx+len(w)))
# abed :: s[3:7]
# abc :: s[9:12]
# bcdf :: s[11:15]