Karp 的技术博客

AC 自动机(Aho-Corasick 自动机)是一种用于多模式字符串匹配的高效算法。它结合了 Trie 树和 KMP 算法的思想,能够在 O(n + m + z) 的时间复杂度内完成字符串匹配,其中 n 是文本长度,m 是所有模式串的总长度,z 是匹配结果的数量。

1. AC 自动机的基本思想

AC 自动机主要由以下几个部分组成:

  • Trie 树:用于存储所有的模式串。
  • 失败指针:用于处理不匹配的情况,类似于 KMP 算法中的部分匹配表。
  • 输出函数:用于记录匹配的模式串。

2. AC 自动机的构建步骤

2.1 插入模式串

首先,我们构建一个 Trie 树,将所有要匹配的模式串插入到树中。

输入模式串: "he", "she", "his", "hers"

Trie 树结构:

        root
        /  \
       h    s
      /      \
     e        h
             / \
            i   e
           /
          s

2.2 构建失败指针

接下来,我们为 Trie 树中的每个节点建立失败指针。如果当前节点无法匹配下一个字符,则通过失败指针回溯到更高层次的节点。

构建失败指针的步骤:

  1. 使用 BFS 遍历 Trie 树,初始化根节点的失败指针为自身。
  2. 对于每个节点,遍历其子节点,设置失败指针。
失败指针示例:

        root
        /  \
       h    s
      /|    |
     e |    h
        |   / \
        |  i   e
        | /
        s
  • 节点 h 的失败指针指向 root
  • 节点 s 的失败指针指向 root
  • 节点 e 的失败指针指向 root
  • 节点 i 的失败指针指向 h(因为 hi 的最长前缀)。
  • 节点 hers 的失败指针指向 hers 本身。

2.3 匹配过程

在匹配过程中,从根节点开始,逐字符遍历文本字符串。如果当前字符在当前节点的子节点中,则继续向下遍历;如果不在,则通过失败指针回溯,直到找到匹配或到达根节点。

3. 示例代码

以下是 AC 自动机的基本实现模板:

class ACNode:
    def __init__(self):
        self.children = {}
        self.fail = None
        self.output = []

class ACAutomaton:
    def __init__(self):
        self.root = ACNode()

    def insert(self, pattern):
        node = self.root
        for char in pattern:
            if char not in node.children:
                node.children[char] = ACNode()
            node = node.children[char]
        node.output.append(pattern)

    def build_fail_pointers(self):
        from collections import deque
        queue = deque([self.root])
        while queue:
            current = queue.popleft()
            for char, child in current.children.items():
                # Find fail pointer
                fail_node = current.fail
                while fail_node and char not in fail_node.children:
                    fail_node = fail_node.fail
                child.fail = fail_node.children[char] if fail_node else self.root
                # Merge output
                if child.fail:
                    child.output.extend(child.fail.output)
                queue.append(child)

    def search(self, text):
        node = self.root
        results = []
        for char in text:
            while node and char not in node.children:
                node = node.fail
            if node:
                node = node.children[char]
            else:
                node = self.root
            
            if node.output:
                results.extend(node.output)
        return results

# 使用示例
ac = ACAutomaton()
patterns = ["he", "she", "his", "hers"]
for pattern in patterns:
    ac.insert(pattern)
ac.build_fail_pointers()
text = "ushers"
matches = ac.search(text)
print(matches)  # 输出匹配的模式串

版权属于:karp
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
更新于: 2024年10月21日 07:07
0

目录

来自 《AC自动机 算法详解及模板》