概念
AC 自动机(Aho-Corasick 自动机)是一种高效的字符串匹配算法,特别适用于从文本中同时查找多个模式串的场景。它结合了 Trie 树(前缀树)和 KMP(Knuth-Morris-Pratt)算法 的思想,可以在 O(n+m+z) 的时间复杂度内完成任务,其中:
- n:文本长度
- m:所有模式串的总长度
- z:匹配到的总结果数
步骤
-
构建 Trie 树
将所有模式串插入 Trie 树,以便快速进行前缀匹配。 -
构建失配指针
使用广度优先搜索(BFS)为每个节点建立失配指针,使得在匹配失败时可以快速跳转到另一个可能的匹配状态。 -
搜索目标文本
遍历目标文本,利用 Trie 树和失配指针同时匹配多个模式串。
应用场景
-
敏感词过滤:检测并替换敏感词。
-
DNA 序列分析:在基因组序列中查找多个目标序列。
-
网络安全:分析日志中的潜在攻击模式。
-
全文搜索:从文档中快速定位关键词。
示例
在一段文本中匹配多个关键词,并统计每个关键词出现的次数。
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
const int ALPHABET_SIZE = 26; // 假设仅处理小写字母
struct TrieNode {
vector<TrieNode*> children;
TrieNode* fail; // 失配指针
int count; // 表示当前模式串匹配次数
TrieNode() : children(ALPHABET_SIZE, nullptr), fail(nullptr), count(0) {}
};
class ACAutomaton {
private:
TrieNode* root;
public:
ACAutomaton() {
root = new TrieNode();
}
// 插入模式串
void insert(const string& word) {
TrieNode* node = root;
for (char c : word) {
int index = c - 'a';
if (!node->children[index]) {
node->children[index] = new TrieNode();
}
node = node->children[index];
}
node->count++; // 标记模式串的结束
}
// 构建失配指针
void build() {
queue<TrieNode*> q;
root->fail = root; // 根节点的失配指针指向自身
for (int i = 0; i < ALPHABET_SIZE; ++i) {
if (root->children[i]) {
root->children[i]->fail = root;
q.push(root->children[i]);
} else {
root->children[i] = root; // 优化:虚拟节点指向根节点
}
}
while (!q.empty()) {
TrieNode* current = q.front();
q.pop();
for (int i = 0; i < ALPHABET_SIZE; ++i) {
TrieNode* child = current->children[i];
if (child) {
child->fail = current->fail->children[i];
q.push(child);
} else {
current->children[i] = current->fail->children[i];
}
}
}
}
// 匹配文本并统计结果
unordered_map<string, int> search(const string& text, const vector<string>& patterns) {
TrieNode* node = root;
unordered_map<string, int> result;
// 初始化结果计数
for (const string& pattern : patterns) {
result[pattern] = 0;
}
for (char c : text) {
int index = c - 'a';
node = node->children[index];
TrieNode* temp = node;
// 遍历失配链以统计所有可能的匹配
while (temp != root) {
if (temp->count > 0) {
result[patterns[temp->count - 1]] += temp->count;
}
temp = temp->fail;
}
}
return result;
}
};
int main() {
ACAutomaton ac;
vector<string> patterns = {"he", "she", "his", "hers"};
string text = "ahishers";
// 构建 AC 自动机
for (const string& pattern : patterns) {
ac.insert(pattern);
}
ac.build();
// 搜索文本
unordered_map<string, int> result = ac.search(text, patterns);
// 输出匹配结果
for (const auto& entry : result) {
cout << entry.first << ": " << entry.second << endl;
}
return 0;
}