AC自动机构建与匹配需严格预处理词典、确保UTF-8编码、选用双数组Trie、隔离查询状态并支持热更新。
构建前必须预处理词典,否则 fail 指针链会异常
ac 自动机对输入词典极其敏感:含空串、超长串、重复项或控制字符(如 "\x00")会导致 build() 阶段 panic 或跳转逻辑错乱。这不是 bug,是算法本身对前缀结构的强依赖。
- 必须过滤:
""、"\x00"、纯空白字符串(用strings.TrimSpace()后判空) - 建议限制单模式最大长度(如
256),防止节点爆炸 - 务必去重——大小写不同但语义相同的词(如
"password"和"Password")在敏感词场景下应视为同一规则 - 排序推荐按长度升序 + 字典序,能让前缀复用更充分(例如
"user"和"username"会共享前 4 个节点)
别直接传 GBK 文本进 FindAllStringIndex()
Go 字符串默认是 UTF-8 编码,但日志、旧系统导出文本常为 GBK、Big5 或无 BOM 的 ANSI。直接传入会导致 rune 切分错位,匹配位置偏移、漏词,甚至返回负索引或 panic。
- 真实场景中,90% 的“AC 不生效”问题根源在此,而非自动机构建逻辑
- 不要用
golang.org/x/text/encoding在匹配前转码——它引入额外分配和同步开销,且容易在高并发下成为瓶颈 - 正确做法:在读取源头就解码为 UTF-8
[]byte,或改用基于byte的实现(如双数组 Trie 版本),绕过rune层抽象
10 万+ 模式时,别用 map[rune]*Node 构建 Trie
多数开源 AC 库(如 github.com/BobuSumisu/aho-corasick)默认用 map[rune]*Node 存子节点,内存占用随模式数非线性增长,10 万模式下构建耗时常超 2s,GC 压力陡增。
- 生产环境应优先选双数组 Trie(Double Array Trie)实现,比如
github.com/grepner/go-ahocorasick的优化分支或自研版本 - 双数组结构将节点压缩为两个整型切片(
base[]和check[]),内存占用降为原来的 1/10,构建速度提升约 10 倍 - 注意:双数组不天然支持 Unicode(rune),需先做字符映射(如 GBK 字节 → uint16 ID),中文支持需额外映射表
匹配时游标必须 per-query 独立,别复用 State
sync.Pool 对 AC 自动机的 State 复用意义不大——自动机结构只读,但匹配过程中的当前节点指针、路径深度、已匹配长度等状态必须每个查询独占,否则并发匹配会相互覆盖。
- 错误写法:
pool.Get().(*Matcher).Search(text)且不重置内部游标 - 正确做法:每次调用
Search()都从root开始,或显式传入干净的上下文结构体(含current *Node字段) - 若追求极致性能,可预分配一批游标对象池,但池内对象必须在
Put()前清零所有字段,不能残留上一次匹配的状态



















