Java DFA算法

DFA(Deterministic Finite Automaton,确定性有限自动机)是一种常用的算法模型,在Java中广泛应用于敏感词过滤 、字符串匹配、词法分析等场景。它的核心特点是:每个状态对于同一个输入字符,有且只有一个转移状态

基本原理

DFA算法通过构建一个状态转移图(通常用嵌套Map或数组表示)来快速匹配关键词。相比于简单遍历匹配,时间复杂度为 O(n),n为待匹配文本长度,与关键词数量无关。

Java实现示例

1. 构建DFA节点模型

java 复制代码
import java.util.*;

public class DFAFilter {
    // 根节点
    private Map<Character, Object> rootNode = new HashMap<>();
    
    // 敏感词结束标识
    private static final String END_FLAG = "isEnd";
    
    /**
     * 添加关键词到DFA中
     * @param keyword 关键词
     */
    public void addKeyword(String keyword) {
        if (keyword == null || keyword.isEmpty()) return;
        
        Map<Character, Object> currentMap = rootNode;
        
        for (int i = 0; i < keyword.length(); i++) {
            char c = keyword.charAt(i);
            
            // 获取下一层节点
            Map<Character, Object> nextMap = 
                (Map<Character, Object>) currentMap.get(c);
            
            if (nextMap == null) {
                nextMap = new HashMap<>();
                currentMap.put(c, nextMap);
            }
            
            currentMap = nextMap;
            
            // 最后一个字符标记结束
            if (i == keyword.length() - 1) {
                currentMap.put(END_FLAG, true);
            }
        }
    }
    
    /**
     * 批量添加关键词
     */
    public void addKeywords(List<String> keywords) {
        for (String keyword : keywords) {
            addKeyword(keyword);
        }
    }
}

2. 匹配敏感词

java 复制代码
/**
 * 检查文本是否包含敏感词,并返回第一个匹配的敏感词
 */
public String check(String text) {
    if (text == null || text.isEmpty()) return null;
    
    for (int i = 0; i < text.length(); i++) {
        int matchCount = 0;
        Map<Character, Object> currentMap = rootNode;
        char c;
        
        for (int j = i; j < text.length(); j++) {
            c = text.charAt(j);
            currentMap = (Map<Character, Object>) currentMap.get(c);
            
            if (currentMap == null) {
                break; // 匹配失败,重新从下一个字符开始
            }
            
            matchCount++;
            
            // 检查是否匹配到完整关键词
            if (currentMap.containsKey(END_FLAG)) {
                return text.substring(i, i + matchCount);
            }
        }
    }
    return null;
}

/**
 * 替换文本中的敏感词
 * @param text 原始文本
 * @param replaceChar 替换字符
 * @return 替换后的文本
 */
public String replace(String text, char replaceChar) {
    if (text == null || text.isEmpty()) return text;
    
    StringBuilder result = new StringBuilder(text);
    List<int[]> positions = new ArrayList<>();
    
    for (int i = 0; i < text.length(); i++) {
        int matchCount = 0;
        Map<Character, Object> currentMap = rootNode;
        char c;
        
        for (int j = i; j < text.length(); j++) {
            c = text.charAt(j);
            currentMap = (Map<Character, Object>) currentMap.get(c);
            
            if (currentMap == null) break;
            
            matchCount++;
            
            if (currentMap.containsKey(END_FLAG)) {
                positions.add(new int[]{i, i + matchCount});
                i = i + matchCount - 1; // 跳过已匹配部分
                break;
            }
        }
    }
    
    // 执行替换
    for (int[] pos : positions) {
        for (int i = pos[0]; i < pos[1]; i++) {
            result.setCharAt(i, replaceChar);
        }
    }
    
    return result.toString();
}

3. 完整示例

java 复制代码
public class DFATest {
    public static void main(String[] args) {
        DFAFilter filter = new DFAFilter();
        
        // 添加敏感词
        filter.addKeywords(Arrays.asList(
            "色情", "暴力", "赌博", "毒品", "法轮功"
        ));
        
        // 测试文本
        String text = "这个网站包含色情和暴力内容,还有赌博信息。";
        
        // 检查敏感词
        String sensitive = filter.check(text);
        System.out.println("检测到敏感词: " + sensitive);
        
        // 替换敏感词
        String filtered = filter.replace(text, '*');
        System.out.println("过滤后: " + filtered);
    }
}

输出:

检测到敏感词: 色情
过滤后: 这个网站包含**和**内容,还有**信息。

优化版本:支持跳过特殊字符

java 复制代码
/**
 * 增强版:支持忽略特殊字符(如空格、标点)
 */
public class AdvancedDFAFilter {
    private Map<Character, Object> rootNode = new HashMap<>();
    private static final String END_FLAG = "isEnd";
    
    // 忽略字符集合(空格、标点符号等)
    private Set<Character> ignoreChars = new HashSet<>(
        Arrays.asList(' ', ',', '.', '!', '?', ';', ':', '、', ',', '。', '!', '?')
    );
    
    public void addKeyword(String keyword) {
        Map<Character, Object> currentMap = rootNode;
        
        for (char c : keyword.toCharArray()) {
            currentMap = (Map<Character, Object>) currentMap.computeIfAbsent(
                c, k -> new HashMap<>()
            );
        }
        currentMap.put(END_FLAG, true);
    }
    
    public String check(String text) {
        for (int i = 0; i < text.length(); i++) {
            int matchCount = 0;
            Map<Character, Object> currentMap = rootNode;
            
            for (int j = i; j < text.length(); j++) {
                char c = text.charAt(j);
                
                // 跳过忽略字符
                if (ignoreChars.contains(c)) {
                    continue;
                }
                
                currentMap = (Map<Character, Object>) currentMap.get(c);
                
                if (currentMap == null) break;
                
                matchCount++;
                
                if (currentMap.containsKey(END_FLAG)) {
                    return text.substring(i, j + 1);
                }
            }
        }
        return null;
    }
}

性能优化建议

  1. 使用数组代替HashMap :当字符集有限(如纯英文)时,可改用Map<Character, Node>Node[] children数组

  2. 内存优化 :使用char[]int[]手动实现紧凑存储

  3. 并行处理:大文本可分片并行匹配

  4. 缓存结果:对于重复文本缓存匹配结果

应用场景

  • ✅ 敏感词过滤系统

  • ✅ 垃圾邮件检测

  • ✅ SQL注入检测

  • ✅ 代码语法高亮

  • ✅ 正则表达式引擎底层实现

DFA算法在Java中实现简单、性能优秀,尤其适合需要高性能字符串匹配的场景,相比之下比遍历关键词列表快很多。

相关推荐
fie88892 小时前
LBP + HOG 特征检测与识别 MATLAB 实现
数据结构·算法·matlab
Yukinaaaa2 小时前
以“轮盘数组”思维彻底搞懂并实现阻塞队列
java·服务器·ide·安全·javaee·阻塞队列·轮盘数组
海天鹰2 小时前
图片去黑边算法
qt·算法
留白_2 小时前
pandas文件读取与存储
开发语言·python·pandas
我的xiaodoujiao2 小时前
API 接口自动化测试详细图文教程学习系列25--继续处理testCase中的数据
python·学习·测试工具·pytest
Wang ruoxi2 小时前
Pygame 小游戏——节奏点击
python·pygame
夕除2 小时前
AOP 实现 Redis 缓存切面解析
java·开发语言·python
库拉大叔2 小时前
工具调用效率对比实测:GPT-5.5与Gemini 3.5 Flash性能评估
java·前端·人工智能
我是唐青枫2 小时前
Java MyBatis 实战指南:XML 映射、动态 SQL 与数据访问层设计
java·mybatis