敏感词匹配DFA算法

算法简介与场景介绍

DFA算法,中文全称为确定性有穷自动机。它的基本思想是构建一个有穷自动机,当用户输入文本时,通过自动机的状态转换来快速匹配敏感词。具体特征是,有一个有效状态的集合和一些从一个状态通向另一个状态的边,每条边上标记一个符号,其中一些状态是初态,一些状态是终态。只有从初态经过正确的状态转移到终态的词才能被认定为敏感词。当然我们也可以将整个DFA模型看成是树型结构,终态对应的都是叶子节点,初态可以看成是某个根节点,只有完全匹配到叶子节点才算匹配成功。最近我在设计我们线上刷题网站的时候,有一个社区讨论模块,大家可以在上面发布动态、发布评论回复,这就难免涉及到一些敏感词的评论,为了构建和谐健康的讨论社区,我们在这边需要对用户发布的评论回复内容进行敏感词的检测。那么在这种场景下,我们就考虑到了使用DFA算法来进行敏感词的快速匹配。

算法实现

DFA模型的构建

模型的构建可以看成是一棵树的构建,树的每个节点都是敏感词中的一个字符,我们以词“傻波一”为例,来构建一个DFA:

1.初始状态:S0

2.状态S1匹配到"傻"

3.状态S2匹配到"波"

4.状态S3匹配到"一"

这里我们只在谈DFA模型构建大体思路,具体使用的数据结构在这里并没有给出,说是树型结构是为了更好地理解这里面敏感词的转换逻辑。我们给出一个示意图来更直观地展示这一过程:

 

 

这是一个有两个简单敏感词地DFA模型,当匹配到的词是"傻"时进入左边的S0状态,等待下一个状态的转换触发条件,比如出现"子"或者"波"进入下一个状态,在等待状态转移的过程中,如果出现的不是"子"或者"波"那么就会回退到初态,也就是顶层节点,等待进入左边节点的S0或者右边的S0,一直到出现"子"或者连着出现了"波"和"一"这样就匹配到了终态,判定当前的词为敏感词。 

代码的构建

在这里,我们将实现构建DFA的方法、删除词库中的敏感词的方法以及使用DFA算法匹配敏感词的方法,帮助大家进一步理解。首先来说一下我们构建DFA用到的数据结构,这边主要使用一个Map集合来标示词节点,键表示为词节点,值为这个词节点后续的词节点的信息,也是同样的Map集合来存储,这个Map集合的形式和前面说的也是一致的,这就像是树节点一样:一个树的根节点是TreeNode类型,有左右孩子,然后它的左右孩子也是TreeNode。这边的思想与这个是一致的。

首先是敏感词的DFA构建,根据上面的描述,算法代码如下显示:

public class DFAFilter {
    private final Map<Character, Map> wordMap = new HashMap<>();
    public void addWord(String word) {
        Map<Character, Map> nowMap = wordMap;
        for (int i = 0; i < word.length(); i++) {
            char keyChar = word.charAt(i);
            Map<Character, Map> newWordMap = nowMap.get(keyChar);
            if (newWordMap == null) {
                newWordMap = new HashMap<>();
                nowMap.put(keyChar, newWordMap);
            }
            nowMap = newWordMap;
        }
        nowMap.put(' ', new HashMap<>()); // 使用空格字符表示 isEnd 标志
    }
}

其次是敏感词的检测,我们这边直接将输入的文本中的敏感词用*代替了,更符合显示场景的需求:

    public String containsSensitiveWord(String text){
        StringBuilder stringBuilder = new StringBuilder(text);

        List<String> result=new ArrayList<>();
        for(int i=0;i<stringBuilder.length();i++){
            Map<Character, Map> cur = wordMap;
            int startIndex=i;
            for(int j=i;j<stringBuilder.length();j++){
                char c = stringBuilder.charAt(j);
                cur=cur.get(c);
                if(cur==null)
                    break;
                if(cur.containsKey(' ')){
                    String substring = stringBuilder.substring(startIndex, j + 1);
                    if(!result.contains(substring))
                        result.add(substring);
                    i=j;
                    for(int k=startIndex;k<=j;k++){
                        stringBuilder.replace(k,k+1,"*");
                    }
                }

            }
        }
        return stringBuilder.toString();
    }

当然如果我们需要删除之前录入的敏感词,核心思想就是遍历这个Map集合,删除敏感词的最后一个词,然后回溯删除不必要的词,删除逻辑是删除那些没有后续字符并且没有终止标识符的词:

public void removeWord(Collection<String> wordList) {
        if (wordList == null || wordList.isEmpty()) {
            return;
        }

        for (String word : wordList) {
            Map<Character, Map> nowMap = wordMap;
            Stack<Map<Character, Map>> stack = new Stack<>();
            Stack<Character> charStack = new Stack<>();

            for (int i = 0; i < word.length(); i++) {
                char keyChar = word.charAt(i);
                Map<Character, Map> nextMap = nowMap.get(keyChar);
                if (nextMap == null) {
                    // 敏感词不存在
                    return;
                }
                stack.push(nowMap);
                charStack.push(keyChar);
                nowMap = nextMap;
            }

            if (nowMap.containsKey(' ')) {
                nowMap.remove(' ');
            }

            // 回溯清理无用节点
            for (int i = word.length() - 1; i >= 0; i--) {
                Map<Character, Map> currentMap = stack.pop();
                char currentChar = charStack.pop();

                // 如果当前字符对应的 Map 为空,则删除该字符节点
                if (nowMap.isEmpty()) {
                    currentMap.remove(currentChar);
                }

                nowMap = currentMap;
            }
        }
    }

那么我们完成了DFA模型的构建、利用DFA模型的状态转换思想进行敏感词检测以及删除敏感词方法。

总结

DFA算法在敏感词检测场景有着比较好的展现效果,其核心思想在于对给定敏感词构建确定性有穷自动机,这边以Map集合的形式构建了一个类似于树型结构的DFA,包括初态中间态以及终态,根据是否出现满足状态转移的条件来推进下一个状态,到达终态判定为敏感词。在我们的刷题网站中的社区模块里,用户发布动态内容和评论回复的敏感词检测就使用到了DFA算法。上面给出了我们实现的DFA算法的核心功能。

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值