何为字典树?
字典树(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
运行效果
输入:
色的 暴 力的赌的多 对多赌 博的的暴力的色 情多对多色情狂
输出:
色的 ***的赌的多 对多***的的***的***多对多***狂其中完美的规避了空格干扰
文章来源: 字典树遍历实现字符串敏感词替换