Java 实现 DFA 算法(敏感词过滤场景)
DFA(确定性有限自动机) 是一种高效字符串匹配算法,核心是将敏感词构造成 Trie 前缀树,匹配时沿状态树单次遍历文本(O (n) 复杂度),不受词库规模影响。
一、DFA 核心原理
DFA 用五元组定义:
M=(Q, Σ, δ, q₀, F)- Q:有限状态集(树节点)
- Σ:输入字符集(文本字符)
- δ:状态转移(节点子节点映射)
- q₀:根节点(初始状态)
- F:终止状态(敏感词结尾标记)
构建:将敏感词逐字符插入树,共享前缀、结尾标记
匹配:遍历文本,沿树转移;到终止态即命中敏感词
isEnd
二、Java 完整实现(推荐)
1. 节点类(TrieNode)
import java.util.HashMap; import java.util.Map; /** * DFA 节点(Trie树节点) */ public class DfaNode { // 子节点:字符 -> 下一个节点 private final Map<Character, DfaNode> children = new HashMap<>(); // 是否为敏感词结尾 private boolean isEnd = false; // 添加子节点 public void addChild(char c, DfaNode node) { children.put(c, node); } // 获取子节点 public DfaNode getChild(char c) { return children.get(c); } // 判断是否包含子节点 public boolean containsChild(char c) { return children.containsKey(c); } // getter & setter public boolean isEnd() { return isEnd; } public void setEnd(boolean end) { isEnd = end; } }
2. DFA 过滤器核心类
import java.util.Set; /** * DFA 敏感词过滤器 */ public class DfaSensitiveFilter { // 根节点 private final DfaNode root = new DfaNode(); /** * 初始化:批量添加敏感词,构建DFA树 */ public void init(Set<String> sensitiveWords) { if (sensitiveWords == null || sensitiveWords.isEmpty()) { return; } for (String word : sensitiveWords) { addWord(word); } } /** * 单个添加敏感词到DFA树 */ public void addWord(String word) { if (word == null || word.trim().isEmpty()) { return; } DfaNode current = root; char[] chars = word.toCharArray(); for (char c : chars) { // 不存在则新建节点 if (!current.containsChild(c)) { current.addChild(c, new DfaNode()); } // 进入下一层 current = current.getChild(c); } // 标记词尾 current.setEnd(true); } /** * 检查文本是否包含敏感词(存在返回true) */ public boolean containsSensitive(String text) { if (text == null || text.trim().isEmpty()) { return false; } char[] chars = text.toCharArray(); int len = chars.length; for (int i = 0; i < len; i++) { DfaNode current = root; // 从i开始逐字符匹配 for (int j = i; j < len; j++) { current = current.getChild(chars[j]); if (current == null) { break; // 不匹配,退出内层循环 } if (current.isEnd()) { return true; // 命中敏感词 } } } return false; } /** * 过滤敏感词(替换为*) */ public String filter(String text) { if (text == null || text.trim().isEmpty()) { return text; } char[] chars = text.toCharArray(); int len = chars.length; StringBuilder result = new StringBuilder(); int i = 0; while (i < len) { DfaNode current = root; int matchEnd = -1; // 寻找最长匹配 for (int j = i; j < len; j++) { current = current.getChild(chars[j]); if (current == null) { break; } if (current.isEnd()) { matchEnd = j; // 记录匹配结束位置 } } if (matchEnd != -1) { // 替换为* for (int k = i; k <= matchEnd; k++) { result.append('*'); } i = matchEnd + 1; } else { // 保留原字符 result.append(chars[i]); i++; } } return result.toString(); } }
3. 测试类
import java.util.HashSet; import java.util.Set; public class DfaTest { public static void main(String[] args) { // 1. 初始化敏感词库 Set<String> words = new HashSet<>(); words.add("赌博"); words.add("毒品"); words.add("诈骗"); words.add("色情"); // 2. 构建DFA过滤器 DfaSensitiveFilter filter = new DfaSensitiveFilter(); filter.init(words); // 3. 测试检测 String text1 = "远离赌博毒品,拒绝诈骗色情"; System.out.println("是否包含敏感词:" + filter.containsSensitive(text1)); // true // 4. 测试过滤 String text2 = "这里有赌博信息和色情内容,小心诈骗"; String filtered = filter.filter(text2); System.out.println("过滤前:" + text2); System.out.println("过滤后:" + filtered); // 输出:这里有**信息和**内容,小心**
String text3 = "我是骗子王,请勿打扰";
String filtered2 = filter.filter(text3);
System.out.println("过滤前:" + text3); // 过滤前:我是骗子王,请勿打扰
System.out.println("过滤后:" + filtered2); // 过滤后:我是***,请勿打扰
}
}
四、核心特点与优化
1. 优点
- 高效:匹配 O(n)(n = 文本长度)
- 稳定:速度不受敏感词数量影响
- 共享前缀:节省内存、构建快
2. 常用优化
- 大小写忽略:添加 / 匹配时统一转小写
- 特殊字符过滤:跳过空格、标点、特殊符号
- 最长匹配:优先匹配长敏感词(如 “广告词” 优先于 “广告”)
- 并发安全:初始化用双重检查锁,更新时重建树
- 动态刷新:支持运行时增删敏感词
五、适用场景
- 内容审核、评论过滤、聊天监控
- 日志关键词扫描、规则匹配
- 词法分析、关键字检索、文本预处理
有些事情,没经历过不知道原理,没失败过不明白奥妙,没痛苦过不了解真谛。临渊羡鱼,不如退而结网!

浙公网安备 33010602011771号