当我们在一段文字中查找多个关键词时,常规的字符串匹配算法可能会变得低效而耗时。然而,AC自动机(Aho-Corasick automaton)算法作为一种高效的字符串匹配算法,为我们提供了一种快速而可靠的解决方案。它像一台智能的文字扫描器,能够在大规模文本中快速定位多个关键词的出现位置。无论是在搜索引擎、敏感词过滤还是文本处理等领域,AC自动机都扮演着重要的角色。
通过构建一个特殊的数据结构——Trie树,并利用失败指针的智能跳转机制,AC自动机实现了高效的字符串匹配。它不仅能够快速找到关键词的匹配位置,还能够处理大规模关键词列表的情况。这种算法的应用广泛,涵盖了搜索引擎中的关键词搜索、敏感词过滤和语法分析等多个领域。
在本文中,我们将深入探讨AC自动机的原理和关键特性。我们将了解它是如何构建Trie树以及如何利用失败指针来提高匹配效率。我们还将讨论AC自动机在不同领域的应用场景,并提供实际代码示例,帮助读者更好地理解和应用这一强大的算法。
AC自动机是一种强大的字符串匹配算法,它可以同时匹配多个模式串,并且在处理大规模文本时具有高效的速度。
AC自动机算法的基本思想是构建一个有限状态机,其中每个状态表示当前已经匹配的模式串的前缀。算法通过将多个模式串以一个树的形式组织起来,构建一个Trie树(也称为关键字树或前缀树),并利用失败指针(failure links)进行状态转移。
在AC自动机中,每个状态节点都有一个失败指针,指向跟当前节点相似的已匹配的最长前缀。当在文本中匹配失败时,算法将根据失败指针的指引,跳转到下一个可能的匹配位置,从而提高匹配效率。
AC自动机算法的时间复杂度为O(n + m + z),其中n是文本长度,m是所有模式串的总长度,z是匹配结果的数量。由于AC自动机利用了Trie树和失败指针的特性,可以高效地处理大量模式串的匹配问题。
假设你是一位班级的班主任,你需要在学生的作文中查找一些关键词,比如"苹果"、"香蕉"和"橙子"。为了提高效率,你决定使用AC自动机。
首先,你需要创建一个AC自动机。你将把关键词列表放入一张表格中,这就是我们的"关键词表"。每个关键词都是一行,按照从左到右的顺序逐字添加到表格中。如果两个关键词的前缀相同,它们将在表格中共享相同的部分。我们使用一个特殊的标记来表示每个关键词的结尾。
接下来,你要构建关键词表中关键词之间的连接。这些连接就像是指向下一个字符的箭头。箭头的路径表示我们在文本中匹配关键词时的移动方向。
当你拿到学生的作文时,你会从作文的开头开始阅读。你在AC自动机的根节点开始。如果你在关键词表中找到了与作文的第一个字符匹配的关键词,你就跳到这个关键词的位置,并继续读下一个字符。如果没有匹配的关键词,你就回到根节点,并读取作文的下一个字符。
如果你发现当前字符在关键词表中没有匹配,那么你会遵循一个特殊的"失败指针"来移动到另一个节点。这个失败指针告诉你从哪个位置继续匹配,避免了从头开始的重复比较。
你会一直这样在作文中前进,直到你读完整篇作文。在这个过程中,如果你到达了关键词表中的某个关键词的结尾,那么你就知道在作文中找到了一个匹配的关键词。
最后,你将回顾你找到的所有匹配,记录它们的起始位置和结束位置。这样,你就能准确地知道在作文中哪些地方出现了关键词。
关于AC自动机:
- AC自动机的原理
- 构建Trie树:AC自动机通过构建一个Trie树来存储多个模式串,利用树节点之间的关系表示模式串的前缀和匹配状态。
- 失败指针:每个状态节点都有一个失败指针,指向与当前节点相似的已匹配的最长前缀。在匹配失败时,根据失败指针进行状态转移,提高匹配效率。
- AC自动机的关键特性
- 高效匹配:AC自动机能够同时匹配多个模式串,且时间复杂度为O(n + m + z),其中n为文本长度,m为所有模式串的总长度,z为匹配结果的数量。
- 失败指针优化:通过利用失败指针,AC自动机可以快速跳转到下一个可能的匹配位置,避免不必要的回溯,提高匹配速度。
- 空间效率:AC自动机的空间复杂度相对较低,只需存储模式串的前缀和一些额外指针信息,适用于处理大规模模式串的场景。
- AC自动机的应用场景
- 文本处理:AC自动机广泛应用于文本搜索、关键词过滤和敏感词检测等任务,能够快速准确地匹配多个模式串。
- 模式识别:AC自动机在模式识别领域有重要应用,例如DNA序列匹配、图像识别等,可以高效地搜索多个模式串的出现。
- 语法分析:AC自动机在编译原理中的语法分析阶段,用于处理词法分析器生成的标记流,识别出语法中的关键词和短语。
基于python实现AC自动机
class TrieNode():
def __init__(self):
self.child = {}
self.failto = None
self.is_word = False
'''
下面节点值可以根据具体场景进行赋值
'''
self.str_ = ''
self.num = 0
class AhoCorasickAutomation:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = TrieNode()
def buildTrieTree(self, wordlst):
for word in wordlst:
cur = self.root
for i, c in enumerate(word):
if c not in cur.child:
cur.child[c] = TrieNode()
ps = cur.str_
cur = cur.child[c]
cur.str_ = ps + c
cur.is_word = True
cur.num += 1
def build_AC_from_Trie(self):
queue = []
for child in self.root.child:
self.root.child[child].failto = self.root
queue.append(self.root.child[child])
while len(queue) > 0:
cur = queue.pop(0)
for child in cur.child.keys():
failto = cur.failto
while True:
if failto == None:
cur.child[child].failto = self.root
break
if child in failto.child:
cur.child[child].failto = failto.child[child]
break
else:
failto = failto.failto
queue.append(cur.child[child])
def ac_search(self, str_):
cur = self.root
result = {}
i = 0
n = len(str_)
while i < n:
c = str_[i]
if c in cur.child:
cur = cur.child[c]
if cur.is_word:
temp = cur.str_
result.setdefault(temp, [])
result[temp].append([i - len(temp) + 1, i, cur.num])
'''
处理所有其他长度公共字串
'''
fl = cur.failto
while fl:
if fl.is_word:
temp = fl.str_
result.setdefault(temp, [])
result[temp].append([i - len(temp) + 1, i, cur.failto.num])
fl = fl.failto
i += 1
else:
cur = cur.failto
if cur == None:
cur = self.root
i += 1
return result
acTree = AhoCorasickAutomation()
acTree.buildTrieTree(['abcdef', 'abhab', 'bcd', 'cde', 'cd', 'cdfkcdf', 'cde'])
acTree.build_AC_from_Trie()
res = acTree.ac_search('bcabcdebcedfabcdefababkabhabkcdbcde')
print(res)
Python 库 ahocorasick 封装了 AC 自动机,简单易用,而且解决了 UTF-8 字符编码问题。
import ahocorasick
def build(patterns):
trie = ahocorasick.Automaton()
for index, word in enumerate(patterns):
trie.add_word(word, (index, word))
trie.make_automaton()
return trie
if __name__ == '__main__':
patterns = ['中国', '山东省', '威海市', '山东大学', '威海校区']
text = '山东大学威海校区坐落于美丽滨城威海市。'
trie = build(patterns)
for each in trie.iter(text):
print(each)
输出结果:
(3, (3, '山东大学'))
(7, (4, '威海校区'))
(17, (2, '威海市'))