敏感词过滤算法:前缀树算法

敏感词过滤算法:前缀树算法背景平时我们在逛贴吧、牛客网的时候,我们经常可以看到一些形如“***”的符号,通过上下文,我们也可以很容易猜到这些词原来是骂人的话,只是被系统和谐了。那么这是如何实现的呢?作为普通人,我们最先想到的一种办法就是把所有敏感串存入一个列表中,然后用户每发一条内容后台就把该内容与敏感串列表的每一项进行匹配,然后把匹配的字符进行和谐。显然这样的效率是很低的。非常影响性能,那么我们有没有其他的算法…

大家好,又见面了,我是你们的朋友全栈君。

背景

平时我们在逛贴吧、牛客网的时候,我们经常可以看到一些形如 “***”的符号,通过上下文,我们也可以很容易猜到这些词原来是骂人的话,只是被系统和谐了。那么这是如何实现的呢?作为普通人,我们最先想到的一种办法就是把所有敏感串存入一个列表中,然后用户每发一条内容后台就把该内容与敏感串列表的每一项进行匹配,然后把匹配的字符进行和谐。显然这样的效率是很低的。非常影响性能,那么我们有没有其他的算法呢?这就是我这篇博文打算介绍的。

原理讲解

1.首先建立个敏感词前缀树

敏感词过滤算法:前缀树算法

根节点为空

2.准备好待处理字符串: 哈哈大王八子大猪蹄子哦 ,声明三个指针,分别指向前缀树的根节点以及待处理字符串的开始字符

敏感词过滤算法:前缀树算法

3.position指向的字符与根节点的所有子节点进行匹配,不匹配,position 和 begin分别指向待处理字符串的下一个字符,tempNode依旧指向 根节点

敏感词过滤算法:前缀树算法

4.依旧不匹配,position 和begin继续向前走一位,指向“”,treeNode依旧指向根节点

敏感词过滤算法:前缀树算法 

5.此时 根节点有一个子节点 与 position指向的字符相等,都为‘’,则tempNode 指向该节点,同时position前进一步,指向‘

敏感词过滤算法:前缀树算法

6.此时把position指向的‘’ 和 tempNode的所有子节点进行匹配,匹配失败,说明 从begin起头所有串是不存在敏感词的,可以直接输出。此时begin前进一位,position回退到begin的位置,tempNode回退到根节点

敏感词过滤算法:前缀树算法

7.此时再把position指向的‘王’与tempNode的所有子节点进行匹配,匹配成功,所以tempNode指向该节点,同时position前进一位,指向’

敏感词过滤算法:前缀树算法

8.此时再把position指向的‘’ 与tempNode的所有子节点进行匹配,匹配成功,此时tempNode 指向它的子节点‘’,同时position前进一位。

敏感词过滤算法:前缀树算法 

9.继续把position指向的字符与tempNode的所有子节点进行匹配,匹配失败。说明以begin起头的不存在非法字符,可以加入到结果集中。 此时begin向前走一位,position回退到begin的位置,同时tempNode回退到根节点。

敏感词过滤算法:前缀树算法

10.同理,可以发现子’子’不匹配,则直接把它加入结果集,同时position 和begin 向前走一位,tempNode指向根节点。

此时position指向 ‘’,与tempNode的所有 子节点进行匹配,匹配成功,则position和tempNode都走一位,循环执行….

直到position指向‘’,tempNode指向‘蹄’

敏感词过滤算法:前缀树算法

 11.此时把position与tempNode的所有子节点进行匹配,匹配成功,tempNode指向它的子节点‘子’,此时检查发现tempNode是敏感词树的叶子节点,说明从begin+1开始的位置 到 position这段是敏感词,用和谐词替换掉。替换之后position前进一位,begin跳到position的位置,tempNode回退到根节点

敏感词过滤算法:前缀树算法

以上,就是全部流程啦,理解了之后看代码就简单多啦

代码讲解

1.前缀树节点结构

private class TreeNode{

        //是否最后一个字
        private boolean isKeyWordsEnd = false;

        //子节点
        private Map<Character,TreeNode> subNodes = new HashMap<>();

        public void addSubNode(Character key, TreeNode node){
            subNodes.put(key,node);
        }

        public TreeNode getSubNode(Character key){
            return subNodes.get(key);
        }

        public boolean isKeyWordsEnd(){
            return isKeyWordsEnd;
        }

        public void setKeyWordsEnd(Boolean end){
            isKeyWordsEnd = end;
        }
    }

2.构建前缀树的方法

public void addSensitiveWord(String words){

        TreeNode tempNode = rootNode;

        for(int i = 0;  i < words.length(); i++){

            Character c = words.charAt(i);
            if(!isSymbol(c)){
                continue;
            }

            TreeNode node = tempNode.getSubNode(c);
            if (node == null){
                node = new TreeNode();
                tempNode.addSubNode(c,node);
            }
            // 指针移动
            tempNode = node;

            //如果到了最后一个字符
            if(i == words.length() -1){
                tempNode.setKeyWordsEnd(true);
            }

        }

    }

3.算法具体实现

public String filter(String text){

        if (StringUtils.isEmpty(text)){
            return text;
        }

        String sensitiveWords = "***";
        StringBuilder result = new StringBuilder();

        TreeNode tempNode = rootNode;
        int begin = 0;
        int position = 0;

        while (position < text.length()){

            Character c = text.charAt(position);

            //如果非东亚字符,则直接跳过 ??
            if(!isSymbol(c)){ //每次
                if(tempNode == rootNode){
                    result.append(c);
                    begin++;
                }
                position++;
                continue;
            }

            tempNode = tempNode.getSubNode(c);

            //如果匹配失败
            if(tempNode == null){
                //说明以begin起头的那一段不存在非法词汇
                result.append(text.charAt(begin));
                begin++;
                position = begin;
                tempNode = rootNode;
                continue;
            }else if(tempNode.isKeyWordsEnd()){
                //替换敏感词
                result.append(sensitiveWords);

                position++;
                begin = position;
                tempNode = rootNode;
            }else {
                position++;
            }

        }
        result.append(text.substring(begin)); //把剩下的动加入合法集

        return result.toString();
    }

小结

最近一直在做项目,所以有一段时间没写文章了,项目也快完成了,就把在项目中使用的一个算法做了下总结,希望能给读者一些帮助。 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/134721.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月6日 下午12:00
下一篇 2022年6月6日 下午12:00


相关推荐

  • Java Socket实现文件传输

    Java Socket实现文件传输阿里云双 11 服务器优惠 年度最佳优惠 t cn Ai3sE9hJ A 1 核 2GB1M 服务器年 86 元 B 1 核 2GB1M 服务器三年 229 元 C 2 核 4GB3M 服务器三年 799 元 强烈推荐 D 2 核 8GB5M 服务器三年 1399 元以上均需新人才可以参加 同时还有香港服务器年 119 元 最近学 Socket 学上瘾了 就写了一个简单的文件传输程序 客户端设计思路 客户

    2026年3月19日
    2
  • java浅拷贝和深拷贝的区别_python的浅拷贝和深拷贝

    java浅拷贝和深拷贝的区别_python的浅拷贝和深拷贝Java中的对象拷贝(ObjectCopy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去。举例说明:比如,对象A和对象B都属于类S,具有属性a和b。那么对对象A进行拷贝操作赋值给对象B就是:B.a=A.a;B.b=A.b;在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用现有对象的部分或全部数据。Java中的对象拷贝主要分为:浅拷贝(ShallowCopy)、深拷贝(DeepCopy)。先介绍一点铺垫知识:Java中的数据类型分为基本数据类型和.

    2026年4月15日
    7
  • oracle到hive数据类型转换「建议收藏」

    oracle到hive数据类型转换「建议收藏」oracle和hive中的数据类型存在差异,在oracle集成数据到hive中这样的场景下,我们希望在hive中的数据是贴源的,所以在hive中希望创建和oracle结构一致的表。oracle到hive数据类型映射参考如下:selectcasewhent1.column_id=1then’CREATETABLEIFNOTEXISTS’||’project’||’….

    2026年2月8日
    6
  • Java中Map的使用

    Java中Map的使用

    2021年12月5日
    57
  • navicat 2021激活码【在线注册码/序列号/破解码】

    navicat 2021激活码【在线注册码/序列号/破解码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月18日
    49
  • 深度学习常见的问题

    深度学习常见的问题

    2021年11月19日
    54

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号