字典树遍历实现字符串敏感词替换

匿名 (未验证) 提交于 2019-12-03 00:21:02

何为字典树?

字典树(Trie树),又称前缀树,一种Hash树的变种,用于统计,排序和保存大量的字符串,利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较


利用字典树对大段文字进行搜索,查找敏感词并进行打码替换

将敏感词词库以字符的形式保存到字典树中,例如敏感词词库中包含了“色情”、“赌博”、“暴力”,则需要生成这么一棵树:

其中根节点不对应任何字符,尾部节点进行标记,表示这是一个词的结尾。
删选敏感词时,对大段文字进行遍历,同时在字典树中进行查找匹配,找到文字中的敏感词并将敏感词替换为“***”符号,以下是详细代码

构造字典树

构造方案是构建一个Node类来表示各节点,Node的成员变量是一个保存有各子节点的Map,key为单个字符,value为子节点Node:
    //字典树节点,其中包含一个Map,该Map保存了所有子节点,key为节点对应的字符     private class TrieNode{         private boolean end = false;         private Map<Character, TrieNode> subNodes = new HashMap<>();          private void addSubNode(Character key, TrieNode node){             subNodes.put(key, node);         }          private TrieNode getSubNode(Character key){             return subNodes.get(key);         }          private boolean isKeyWordEnd(){             return end;         }          private void setKeyWordEnd(boolean end) {             this.end = end;         }     }

其中end变量表示该节点是否为末尾节点。

输入敏感词词库中的单词:

//对每一个敏感词进行遍历,将该关键字的每个字添加到字典树的每个结点上     private void addWord(String lineTxt){         TrieNode tempNode = rootNode;         for(int i = 0; i<lineTxt.length(); i++){             Character c = lineTxt.charAt(i);             TrieNode subNode = tempNode.getSubNode(c);             if(null == subNode){                 subNode = new TrieNode();                 tempNode.addSubNode(c, subNode);             }             tempNode = subNode;             //如果字符遍历到头则将该节点标记为尾部节点             if(i == lineTxt.length()-1){                 tempNode.setKeyWordEnd(true);             }         }     }

至此,敏感词字典树已经构建完成。

文字敏感词过滤器

输入整段文字,输出经过过滤后的净化版文字:
    public String filter(String text){         if(!StringUtils.hasText(text)){             return text;         }         StringBuilder sb = new StringBuilder(); //存放过滤后文本的StringBuilder         String replacement = "***"; //“马赛克”模板         int begin = 0; //单词遍历起点指针         int position = 0; //文本字符遍历指针         TrieNode tempNode = rootNode; //对应字典树上的遍历指针          //对文本的每个字符进行遍历         while (position<text.length()){             Character c = text.charAt(position);              //如果当前字符是干扰符号,则直接跳过;             // 如果字典树遍历还未开始则将头指针begin++,并且将当前字符添加到暂存区sb中             if(isSymbol(c)){                 position++;                 if(tempNode==rootNode){                     sb.append(c);                     begin++;                 }                 continue;             }              TrieNode subNode;             if(null != (subNode = tempNode.getSubNode(c))){                 //如果当前Node下面包含有c字符对应的Node,则将position指针移动一位                 tempNode = subNode;                 position++;                 //如果包含c字符的subNode已经是结尾Node,则表示begin到position的字符串是敏感词,需要替换为"***"                 //同时将字典树的指针tempNode回归到根结点                 if(subNode.isKeyWordEnd()){                     sb.append(replacement);                     tempNode = rootNode;                     begin = position;                 }                 if(position == text.length()){                     for(int i = begin; i<position; i++){                         sb.append(text.charAt(i));                     }                     break;                 }             }else {                 //如果当前Node下不包含c字符对应的Node,则position指针移动一位,并将begin与position同步                 //同时将字典树的指针tempNode回归到根结点                 //将begin到position之间的字符串添加到暂存区sb                 position++;                 for(int i = begin; i<position; i++){                     sb.append(text.charAt(i));                 }                 begin = position;                 tempNode = rootNode;             }         }         return sb.toString();     } 

其中核心为三个指针:begin、position、tempNode

运行效果

输入:

色的    暴 力的赌的多 对多赌 博的的暴力的色 情多对多色情狂

输出:

色的    ***的赌的多 对多***的的***的***多对多***狂
其中完美的规避了空格干扰

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!