数据结构与算法 AC自动机详细分析及Java实现
AC自动机概述
AC自动机(Aho-Corasick automaton)是一种高效的多模式匹配算法,由Alfred V. Aho和Margaret J. Corasick于1975年发明。它结合了Trie树和KMP算法的思想,能够在O(n + m + z)的时间复杂度内完成多模式匹配,其中n是文本长度,m是所有模式串的总长度,z是匹配次数。
核心概念
- Trie树:存储所有模式串的前缀树结构
- 失败指针(Fail Pointer):类似KMP中的next数组,用于在匹配失败时跳转
- 输出链(Output Links):用于收集所有匹配的模式串
工作流程
- 构建Trie树:将所有模式串插入到Trie中
- 构建失败指针:使用BFS遍历Trie树,为每个节点设置失败指针
- 文本匹配:遍历文本字符,在Trie上移动并收集所有匹配的模式串
Java实现
import java.util.*;
class ACNode {
Map<Character, ACNode> children = new HashMap<>();
ACNode fail = null;
List<String> outputs = new ArrayList<>();
int depth = 0;
}
public class AhoCorasick {
private ACNode root;
private boolean built = false;
public AhoCorasick() {
root = new ACNode();
}
// 插入模式串
public void insert(String pattern) {
if (built) throw new IllegalStateException("AC自动机已构建完成,不能添加新模式");
ACNode node = root;
for (char c : pattern.toCharArray()) {
node.children.putIfAbsent(c, new ACNode());
node = node.children.get(c);
}
node.outputs.add(pattern);
}
// 构建失败指针
public void build() {
if (built) return;
Queue<ACNode> queue = new LinkedList<>();
// 第一层节点(root的直接子节点)的fail指向root
for (ACNode child : root.children.values()) {
child.fail = root;
queue.add(child);
}
// BFS遍历构建失败指针
while (!queue.isEmpty()) {
ACNode current = queue.poll();
for (Map.Entry<Character, ACNode> entry : current.children.entrySet()) {
char c = entry.getKey();
ACNode child = entry.getValue();
// 从当前节点的fail节点开始,查找匹配c的节点
ACNode failNode = current.fail;
while (failNode != null && !failNode.children.containsKey(c)) {
failNode = failNode.fail;
}
if (failNode == null) {
child.fail = root;
} else {
child.fail = failNode.children.get(c);
// 继承fail节点的输出
child.outputs.addAll(child.fail.outputs);
}
queue.add(child);
}
}
built = true;
}
// 在文本中搜索所有模式串
public Map<String, List<Integer>> search(String text) {
if (!built) build();
Map<String, List<Integer>> results = new HashMap<>();
ACNode current = root;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
// 沿着失败指针链查找匹配的子节点
while (current != root && !current.children.containsKey(c)) {
current = current.fail;
}
if (current.children.containsKey(c)) {
current = current.children.get(c);
} else {
current = root;
}
// 收集当前节点的所有输出
for (String pattern : current.outputs) {
int startIndex = i - pattern.length() + 1;
results.computeIfAbsent(pattern, k -> new ArrayList<>()).add(startIndex);
}
}
return results;
}
public static void main(String[] args) {
AhoCorasick ac = new AhoCorasick();
// 添加模式串
String[] patterns = {"he", "she", "his", "hers"};
for (String pattern : patterns) {
ac.insert(pattern);
}
// 构建AC自动机
ac.build();
// 搜索文本
String text = "ushers and he went to his house";
Map<String, List<Integer>> results = ac.search(text);
// 打印结果
System.out.println("文本: \"" + text + "\"");
System.out.println("\n匹配结果:");
for (Map.Entry<String, List<Integer>> entry : results.entrySet()) {
System.out.printf("模式串 \"%s\" 出现在位置: %s%n",
entry.getKey(), entry.getValue());
}
}
}
算法分析
失败指针的作用
失败指针允许算法在匹配失败时快速跳转到下一个可能匹配的位置,避免重新从根节点开始匹配,这是AC自动机高效的关键。
输出链的作用
输出链用于收集所有可能的匹配结果,包括当前节点匹配的模式串以及通过失败指针链继承的模式串。
应用场景
- 敏感词过滤系统
- 生物信息学中的DNA序列匹配
- 网络入侵检测系统
- 文本搜索引擎
- 代码分析工具
优化策略详解
1. 数据结构优化
- 数组存储子节点:使用固定大小数组(128位ASCII)替代Map,减少对象开销
- 模式串索引存储:存储模式串索引而非字符串本身,减少内存占用
- 输出链指针:添加outputLink字段,优化输出收集过程
2. 构建过程优化
- 失败指针构建:优化回溯逻辑,减少不必要的循环
- 输出链构建:在构建失败指针时同时构建输出链
- 批量插入:提供insertAll方法支持批量操作
3. 搜索过程优化
- 回溯优化:减少匹配失败时的回溯次数
- 批量结果收集:通过输出链一次性收集所有匹配结果
- 位置计算优化:使用结束位置和模式串长度计算起始位置
4. 内存优化
- 精简节点结构:只存储必要字段
- 索引替代字符串:减少字符串重复存储
- 避免冗余存储:通过输出链共享输出结果
性能分析
时间复杂度
- 构建阶段:O(m),m为所有模式串总长度
- 搜索阶段:O(n + z),n为文本长度,z为匹配次数
空间复杂度
- O(m),m为所有模式串总长度
- 优化后减少约30-50%内存占用
优化效果
- 搜索速度提升:减少回溯次数和结果收集时间
- 内存占用降低:精简数据结构,使用索引替代字符串
- 批量操作支持:提高初始化效率
高级优化方向
1. 双数组Trie(Double-Array Trie)
- 将Trie树转换为两个数组(base和check)
- 大幅减少内存占用(约原大小的1/3)
- 提高缓存命中率,加速匹配过程
2. 压缩Trie
- 合并后缀相同的节点
- 减少节点数量,优化内存使用
- 适用于模式串有大量公共后缀的场景
3. 并行处理
- 多线程构建失败指针
- 并行文本匹配(分块处理)
- 利用多核处理器提高性能
4. 增量更新
- 支持动态添加/删除模式串
- 局部重建失败指针
- 适用于需要频繁更新模式库的场景
应用场景
- 敏感词过滤系统:实时检测文本中的敏感词
- 生物信息学:DNA序列模式匹配
- 网络安全:入侵检测系统中的特征匹配
- 文本搜索:文档内容检索
- 代码分析:静态代码分析中的模式检测
AC自动机经过优化后,在处理大规模模式匹配问题时,性能显著优于朴素实现,特别适合需要高效匹配大量模式串的场景。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120678

浙公网安备 33010602011771号