【算法】DFA敏感词过滤

最近刚好有群友问到关于敏感词过滤的问题,当时有人给出了一些办法。

1. 利用HashSet,对传多来的字符串进行比较。或者将敏感词存储到数据库或者其他地方,然后和传入的词做匹配。

2. 正则表达式匹配。

上述两个方法不用想肯定都是很慢的。后来有人说道可以利用DFA算法,因此我去研究了一下,增加自己的知识面。

参考博客:

https://www.cnblogs.com/zyguo/p/4705086.html

具体的解释不在写了,本质上是一个状态机,根据state+event --> nextState。了解过工作流的人肯定不陌生状态机这个概念的。围绕这个状态机,可以根据输入的一个词,推导出下一个词是什么。

根据参考博客,稍微修改了下实现,本质一样。

public class Check {

    private static Map localMap = null;

    public static void init(Set<String> keyWordSet){

        localMap = new HashMap(keyWordSet.size());
        String key = null;
        Map nowMap = null;
        Map<String, String> newWorMap = null;

        for(String str : keyWordSet){
            key = str;
            nowMap = localMap;
            for(int i = 0 ; i < key.length() ; i++){
                char keyChar = key.charAt(i);
                Object wordMap = nowMap.get(keyChar);
                if(wordMap != null){
                    //如果存在该key,直接赋值
                    nowMap = (Map) wordMap;
                }else{
                    //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
                    newWorMap = new HashMap<>();
                    //不是最后一个
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }
                if(i == key.length() - 1){
                    //最后一个
                    nowMap.put("isEnd", "1");
                }
            }
        }
    }

    /**
     *  版本一
     * @param txt 需要查找的字符串
     * @param beginIndex 查找开始位置
     * @return
     */
    public static Set<String> getSensitiveWordV1(String txt,int beginIndex){

        //敏感词集合
        Set<String> str = new HashSet<>();
        char word = 0;
        Map nowMap = localMap;
        StringBuilder sb = new StringBuilder();
        for(int i = beginIndex; i < txt.length() ; i++){
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);
            if(nowMap != null){
                sb.append(word); //拼接敏感词
                if("1".equals(nowMap.get("isEnd"))){
                    str.add(sb.toString()); //保存当前敏感词
                    sb.setLength(0);  //清除当前已保存的敏感词                   
                }
            }else {

                //如果查找不到,将初始值赋值给map
                nowMap = localMap;
                //清除当前保存的敏感词,可能保存到一半发现不满足要求,需要清空,以便下次保存
                sb.setLength(0);
            }
        }
        return str;
    }
}

 getSensitiveWordV1这个称作版本一,和博客里是一样的,但是我运行时发现一点问题。

问题一:

假设现在敏感词是“中国人”,“你们”, 待检测字段是“你好啊中国人你们”,会发现只能检测出“中国人”,而“你们”却漏掉了,主要原因就是当读取到“你”这个字时,发现map此时为null了,那么原本从“中”字开始时发现此时应该是检测敏感词了,但是检测到“你”时,发现不符合敏感词规则,所以检测出敏感词是“中国人”,但是下一次循环就从“们”开始了,“你”这个字就华丽丽的溜掉了。

其实这个场景的本质就是两个敏感词联在一起,导致原本检测第一个敏感词时,本来该结束的时候,下一个字时另一个敏感词的开始,被忽略了。

修复代码:

/**
     *  版本二
     * @param txt 需要查找的字符串
     * @param beginIndex 查找开始位置
     * @return
     */
    public static Set<String> getSensitiveWordV2(String txt,int beginIndex){

        //敏感词集合
        Set<String> str = new HashSet<>();
        char word = 0;
        Map nowMap = localMap;

        boolean flag = false;

        StringBuilder sb = new StringBuilder();
        for(int i = beginIndex; i < txt.length() ; i++){
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);
            if(nowMap != null){
                sb.append(word); //拼接敏感词
                flag = true; //标注当前正在进行敏感词过滤
                if("1".equals(nowMap.get("isEnd"))){
                    str.add(sb.toString()); //保存当前敏感词
                    sb.setLength(0);  //清除当前已保存的敏感词
                    flag = false; //标注当前敏感词过滤成功
                    nowMap = localMap; //过滤成功后将敏感词重新赋值给nowMap
                }
            }else {
                if(flag){
                    flag = false;
                    //当前正在进行敏感词过滤,但是进行到一半中断了,
                    // 此时需要回退一位,重新判断此字符是否是另一个敏感词的开始
                    i--;
                }
                //如果查找不到,将初始值赋值给map
                nowMap = localMap;
                //清除当前保存的敏感词,可能保存到一半发现不满足要求,需要清空,以便下次保存
                sb.setLength(0);
            }
        }
        return str;
    }

但是写着我又发现问题二了:

问题二:

假设现在敏感词是“中国人民”,“测试”,只能检测出“中国人民”和“测试”这两个词,但是如果现在有敏感词有“中国人”的话,就检测不出“中国人民”了,这个怎么解决,我暂时还没想好,只能想到针对这种情况的话,DFA应该是没法满足的,因为它是状态机,“中国人”和“中国人民”在状态机里,原本是“民”是最后一个状态,但是现在“人”变成最后一个状态了,导致只能检测到“中国人”就结束了,“民”就丢掉了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值