由于最近项目中需要使用
Python
的Ahocorasick
库进行模式匹配,因此记录该库所使用的到的Aho-Corasick
算法(AC
算法)的概要以及该库在Python
中的具体使用方法。
概述:Aho-Corasick
算法是多模式匹配中的经典算法,目前在实际应用中较多。Aho-Corasick
算法对应的数据结构是Aho-Corasick
自动机,简称AC
自动机Automaton
。该算法能够识别出一个给定的语句中包含了哪些词典库中特定的词语,具有很有的模式匹配作用。
1. Aho-Corasick算法
在介绍该算法之前,我们需要明白的一个概念是Trie
即字典树(又称为单词查找树或者键树)是一种树形结构作为哈希树的变种,典型的应用就是用于统计和排序大量的字符串(起源于对字符串的研究但是不仅限于字符串例如中文语句的关键词提取);另一个需要知道的概念是KMP
即Knuth-Morris-Pratt
字符串查找算法,常用于在一个文本串S
内查找一个模式串P
的出现位置,这个算法由Donald Knuth
、James H. Morris
、Vaughan Pratt
三人于1977年联合发表,故取这3人的姓氏命名此算法。关于这两个概念的详细信息并不在此赘述,而是通过下图来简单介绍一下这两个概念在AC
算法中是如何体现的。
现在假设我们的文本库中存在ACC
、ATC
、CAT
、GCG
四个字符串,那么我们首先可以根据这四个字符串来构造一个Trie
即根节点为空节点,并逐单词构造该树。例如ACC
那么先生成A
这条边指向下一个节点再生成C
指向下一个节点再生成C
指向最后一个空节点,同理对于ATC
而言也是如此,需要注意的是在构造的过程中同一层级某个单词最多只出现一遍,比如ACC
和ATC
都共用A
这条边指向的节点。同理即可构造出蓝色线条和蓝色节点所生成的字典树。
当生成了字典树后,基于KMP
算法来生成所谓的failing link
即回溯路线,该概念的含义是如果运行到某个节点处无法继续运行的话,那么就通过回溯路线回溯后继续运行,这样可能有点抽象,但是在接下来的例子讲解中会显得比较清晰。因此,我们需要考虑的是如何生成failing link
,一般来说遵循以下的规则:
- 根节点
Root
连向自己同时所有与根节点直接相连的节点都指向根节点。 - 对于其他节点
Node
而言,首先判断其父节点是否存在一条回溯路线且指向的节点存在一个与Node
的值相同的子节点,如果存在那么就将自己的回溯路线也指向该子节点,如果不存在那么就继续找该Node
的父节点的父节点依次找到Root
为止。这一条规则可能会比较绕后续也会结合例子详细讲解。
下图就是根据上述规则生成的字典树以及回溯路线,这里我们挑几个比较具有代表性的节点来看,比如AC
节点,沿着AC
路线的C
节点的父节点的回溯路线指向Root
且Root
具有值为C
的子节点因此AC
的回溯路线指向C
,沿着ACC
路线的C
节点的父节点的回溯路线指向C
该C
节点并不存在值为C
的子节点反而只有A
节点,于是沿着ACC
路线的C
节点只能去寻找它父亲的父亲即A
节点,其回溯路线指向Root
并具有一个C
节点于是指向该C
节点。同理可以构造出所有的回溯路线。
当构造出对应的字典树和回溯路线后即可开始使用AC
算法进行字符串的模式匹配,我们假设给定一个GCATCG
字符串判断其包含文本库中的哪些字符串,因此我们可以这样运作,首先沿着字典树存在GC
路线但是该路线的下一个是G
而不是给定字符串中的A
于是我们沿着回溯路线回到C
节点,此时恰好C
节点的下一个节点是A
节点同时再下一个节点是T
节点于是沿着GCGAT
的路线达到了字典树CAT
路线的叶子节点,我们将达到叶子节点视为匹配成功即存在CAT
字符串,此时虽然已经匹配成功但是为了能够继续运作我们仍然继续执行回溯路线即指向AT
节点,那么由于下一个节点刚好C
那么沿着GCGATC
路线达到了ATC
路线的叶子节点因此匹配成功我们认为存在ATC
字符串,继续沿着回溯路线回到C
节点此时我们给定的字符串中下一个节点是G
而运作路线的下一个节点是A
于是回溯到根节点此时已经遍历完成所以我们判断原字符串中存在CAT
和ATC
子串。
下图取自Youtube
上某个博主讲解的视频
视频连接为:https://www.youtube.com/watch?v=O7_w001f58c
2.Python中Ahocorasic库的使用
有了以上的基础后即可开始学习Python
中Ahocorasic
函数库的使用方法了,我的项目需求是通过给定一个文本库中包含各种疾病信息、科室信息、药品信息等等来解析用户的输入中包含了哪些关键词,因此需要使用到该函数库来快速完成需求。
插播一个注意事项,即如果
Python
的版本在3.7
及以上那么可能会发现找不到pyahocorasick
函数库而是只显示r-ahocorasicktrie
函数库,因此如果需要适应低版本的函数库的话需要适应3.7
版本以下的Python
如Python3.6
下面主要介绍该函数库的基本使用步骤:
首先需要通过pip install pyahocorasick
来安装该库
其次需要根据给定的文本库来将内容添加到actree
中来形成词典树
插播一下
Python
中enumerate
函数的使用结果,就是比如['a','ab','abc']
列表作为参数那么就会形成{1:'a',2:'b',3:'c'}
这样的结果因此可以用于快速构建索引
add_word
函数将第一个作为key
构建actree
同时第二个参数作为value
来作为查询的结果
# 往actree中添加数据
actree = ahocorasick.Automaton()
for index, word in enumerate(wordlist):
actree.add_word(word, (index, word))
actree.make_automaton()
最后根据该词典树来查询目标语句中出现在树中的关键词
iter
函数相当于通过实参来查找actree
中的key
并返回该键值对形成的元组
# 使用actree来查询目标语句中出现在actree中的关键词
for i in actree.iter(target_str):
wd = i[1][1] # i的形式为(index,(index,word))
target_wds.append(wd)
补充需要:在某些情况下一个词可能在多个词中出现因此需要将重复出现的词语屏蔽比如如target_wds=['乙肝', '肝硬化', '硬化']
,则stop_wds=['硬化']
那么在得到最终词表的时候就不会将硬化放在词表中而是选择信息量更大的肝硬化。
# 附加功能:可能出现一个词分成几个部分,把这个部分过滤掉
stop_wds = []
for wd1 in target_wds:
for wd2 in target_wds:
if wd1 in wd2 and wd1 != wd2:
stop_wds.append(wd1)
final_wds = [i for i in target_wds if i not in stop_wds]