logo

Aho-Corasick算法

Published on

Aho-Corasick 算法是一种用于多模式字符串匹配的有效算法。它由 Alfred V. Aho 和 Margaret J. Corasick 于 1975 年提出。该算法的主要应用是在一个文本中同时搜索多个模式字符串。

算法原理

Aho-Corasick 算法的核心思想是将所有模式字符串构建成一棵有向图(称为“字典树”或“Trie”),并在此基础上构建一个失败函数(failure function),以便在匹配失败时能够快速转移到下一个可能的匹配状态。

Trie 构建

  1. 插入模式字符串:将所有模式字符串插入到 Trie 中,每个节点代表一个字符。
  2. 标记结束节点:在每个模式字符串的末尾节点进行标记,以便识别完整的模式。

失败函数构建

  1. 初始化失败指针:根节点的失败指针指向自身。
  2. 广度优先搜索:使用广度优先搜索(BFS)遍历 Trie,设置每个节点的失败指针。
  3. 失败指针设置:对于每个节点,失败指针指向当前节点的父节点的失败指针所指向的节点的子节点(如果存在),否则指向根节点。

匹配过程

  1. 初始化:从根节点开始。
  2. 字符匹配:对于文本中的每个字符,沿着 Trie 进行匹配。
  3. 失败转移:如果匹配失败,使用失败指针进行转移,直到找到匹配或回到根节点。
  4. 模式识别:当到达一个标记为结束的节点时,表示找到了一个模式。

时间复杂度

Aho-Corasick 算法的时间复杂度为 O(n + m + z),其中 n 是文本长度,m 是所有模式字符串的总长度,z 是所有匹配出现的总数。这使得它在处理大规模文本和多模式匹配时非常高效。

应用场景

  • 文本过滤:用于敏感词过滤、垃圾邮件检测等。
  • 生物信息学:用于 DNA 序列分析。
  • 网络安全:用于入侵检测系统。

Aho-Corasick 算法因其高效的多模式匹配能力而被广泛应用于各种领域。

示例代码

以下是一个简单的 Aho-Corasick 算法的 Python 实现示例:

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

class AhoCorasick:
    def __init__(self, patterns):
        self.root = TrieNode()
        self.build_trie(patterns)
        self.build_failure_links()

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

    def build_failure_links(self):
        from collections import deque
        queue = deque()
        for node in self.root.children.values():
            node.fail = self.root
            queue.append(node)

        while queue:
            current_node = queue.popleft()
            for char, child_node in current_node.children.items():
                fail_node = current_node.fail
                while fail_node is not None and char not in fail_node.children:
                    fail_node = fail_node.fail
                child_node.fail = fail_node.children[char] if fail_node else self.root
                child_node.output += child_node.fail.output if child_node.fail else []
                queue.append(child_node)

    def search(self, text):
        node = self.root
        results = []
        for i, char in enumerate(text):
            while node is not None and char not in node.children:
                node = node.fail
            if node is None:
                node = self.root
                continue
            node = node.children[char]
            if node.output:
                for pattern in node.output:
                    results.append((i - len(pattern) + 1, pattern))
        return results

# 示例使用
patterns = ["he", "she", "his", "hers"]
text = "ushers"
ac = AhoCorasick(patterns)
matches = ac.search(text)
print(matches)  # 输出: [(1, 'she'), (2, 'he'), (3, 'hers')]