AC自动机比暴力find快得多,单次扫描完成全部匹配,时间复杂度接近O(n+m);需去重、按长度升序插入、BFS初始化fail指针;替换应从右往左处理并避免二次匹配;中文需UTF-32或Unicode码点预处理。

敏感词匹配用 AC 自动机比暴力 find 快得多
直接循环调用 std::string::find 查每个敏感词,文本长、词库大时性能断崖式下跌。AC 自动机(Aho-Corasick)把所有敏感词构建成一棵带失败指针的 Trie,单次扫描就能完成全部匹配,时间复杂度接近 O(n + m)(n 是文本长度,m 是匹配总数),实测 10 万词库+1MB 文本,提速 50 倍以上。
别手写失败指针逻辑——容易漏掉多层回退或重复入队。推荐用成熟实现,比如 aho-corasick 库(C++17,头文件仅需 aho_corasick.hpp),或自己封装基于 std::vector 的紧凑 Trie 节点:
struct Node {
std::array<int, 256> next = {}; // 简化版:ASCII 映射
int fail = 0;
bool is_end = false;
std::vector<size_t> output; // 匹配到的词索引(支持同位置多词)
};
构建 AC 自动机时必须对敏感词去重和排序
重复词会浪费节点和 output 存储;更麻烦的是,若词 A 是词 B 的前缀(如 “色情” 和 “色情网站”),不按长度升序插入,会导致短词的 output 在长词匹配时被覆盖或遗漏。
- 先用
std::set<std::string>去重 - 再按长度升序排序,长度相同时字典序无关紧要
- 插入时记录原始词索引,方便后续定位具体是哪个词命中
- 构建完立即调用 BFS 初始化
fail指针——不能延迟到查询时算
replace 替换时注意避免二次匹配和越界
原地替换(如把“枪支”替成“**”)后,新字符串可能意外触发其他敏感词(例如“支**”又含“支”),或者因长度变化导致下标错位。生产环境强烈建议只做标记或返回位置,替换交给上层决定:
立即学习“C++免费学习笔记(深入)”;
- 用
std::vector<std::pair<size_t, size_t>>返回所有匹配区间(左闭右开) - 若必须替换,先收集全部区间,再从右往左处理(避免左边替换影响右边坐标)
- 不要用
std::string::replace频繁拼接——小字符串还行,大文本建议用std::string_view+ 输出缓冲区
中文敏感词要预处理编码和分词边界
AC 自动机默认按字节匹配,UTF-8 中文一个字占 3 字节,直接塞进去会把“你好”拆成 6 个无效字节节点。两种解法:
- 统一转为 UTF-32(
std::u32string),用char32_t构建 Trie,内存翻 4 倍但逻辑干净 - 保持 UTF-8,但预处理敏感词:用 ICU 或
utf8cpp库正确切分 Unicode 码点,再逐码点插入(不是逐字节) - 完全避开多字节问题:用拼音转换(如“枪支”→“qiangzhi”),但漏掉同音异形词,慎用
另外,单纯字符匹配会误杀(如“王八”在“王八蛋”里命中,但“王八”单独出现才需过滤),真要语义级过滤得接 NLP 分词器,AC 自动机只负责高速初筛。


















