惊艳是算法:AC自动机

当我们在一段文字中查找多个关键词时,常规的字符串匹配算法可能会变得低效而耗时。然而,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, '威海市'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芝士AI吃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值