By
木子雷
阅读数: 次
前言:
Trie树也称为 字典树、单词查找树 ,最大的特点就是共享字符串的公共前缀来达到节省空间的目的了。
然后可以根据它的公共前缀的特性来实现敏感词过滤、自动联想等功能。
抽象出trie树的数据结构:
1、首先来看下trie树的结构图:
从上图可以归纳出Trie树的基本性质:
①根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
②从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
③每个节点的所有子节点包含的字符互不相同。
④从第一字符开始有连续重复的字符只占用一个节点,比如上面的to,和ten,中重复的单词t只占用了一个节点
从上面归纳出的基本性质可以抽象出节点的class属性:
1、是否为叶子节点的标志位 isWord ;
2、既能存储此节点的值也能存储其所有的子节点的 children 数据结构HashMap;
代码奉上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
| package com.lyl.trie; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; public class Trie {
private class Node { public boolean isWord; public HashMap<Character, Node> children; public Node(boolean isWord) { this.isWord = isWord; this.children = new HashMap<>(); } public Node() { this(false); } } private Node root; private int size; public Trie() { this.root = new Node(); this.size = 0; }
public int getSize() { return size; }
public void addBranchesInTrie(String word) { Node cur = root; char[] words = word.toCharArray(); for (char c : words) { if (!cur.children.containsKey(c)) { cur.children.put(c, new Node()); } cur = cur.children.get(c); } if (!cur.isWord) { cur.isWord = true; size++; } }
public boolean contains(String word) { Node cur = root; char[] words = word.toCharArray(); for (char c : words) { if (!cur.children.containsKey(c)) { return false; } cur = cur.children.get(c); } return cur.isWord; }
public String sensitiveWordReplace(String word) { System.out.println("敏感词替换前:" + word); Node cur = root; char[] words = word.toCharArray(); StringBuilder oldTemp = new StringBuilder(); StringBuilder starTemp = new StringBuilder(); for (char c : words) { if (!cur.children.containsKey(c)) { if (Character.isWhitespace(c) && oldTemp.length() > 0) { oldTemp.append(c); starTemp.append("*"); } continue; } if (!cur.isWord) { oldTemp.append(c); starTemp.append("*"); cur = cur.children.get(c); } if (cur.isWord) { word = word.replaceAll(oldTemp.toString(), starTemp.toString()); oldTemp.delete(0, oldTemp.length()); starTemp.delete(0, starTemp.length()); cur = root; } } System.out.println("敏感词替换后:" + word); return word; } private List<String> list = new ArrayList<String>();
public void prefixMatching(String word, Node root) { Node cur = root; char[] words = word.toCharArray(); StringBuilder str = new StringBuilder(); str.append(word); for (int i = 0; i < words.length; i++) { if (!cur.children.containsKey(words[i])) { System.out.println("无关联词!"); return; } cur = cur.children.get(words[i]); } dfs(str, cur); System.out.println("[ " + word + " ]在trie树中的联想词:" + Arrays.toString(list.toArray())); }
public void dfs(StringBuilder word, Node root) { Node cur = root; if (cur.isWord) { list.add(word.toString()); if (cur.children.size() == 0) { return; } } for (Character s : cur.children.keySet()) { word.append(s); dfs(word, cur.children.get(s)); word.delete(word.length() - 1, word.length()); } } public static void main(String[] args) { Trie t = new Trie(); t.addBranchesInTrie("麻痹"); t.addBranchesInTrie("尼玛的"); t.addBranchesInTrie("狗日的"); t.addBranchesInTrie("联想云科技"); t.addBranchesInTrie("联盟"); t.addBranchesInTrie("联和利泰扩招了"); System.out.println("trie树中分枝的个数:" + t.size); String word = "尼玛的"; System.out.println("Trie树中是否存在[ " + word + " ]敏感词: " + t.contains(word)); t.sensitiveWordReplace("衮,尼玛的傻子,你麻痹的,你各狗日的,早晚揍死你。"); t.prefixMatching("联", t.root); } }
|
代码运行输出:
1 2 3 4 5
| trie树中分枝的个数:6 Trie树中是否存在[ 尼玛的 ]敏感词: true 敏感词替换前:衮,尼玛的傻子,你麻痹的,你各狗日的,早晚揍死你。 敏感词替换后:衮,***傻子,你**的,你各***,早晚揍死你。 [ 联 ]在trie树中的联想词:[联想云科技, 联和利泰扩招了, 联盟]
|
参考资料:
1、【图解算法面试】记一次面试:说说游戏中的敏感词过滤是如何实现的?
2、 前缀树(Trie)原理及Java实现
3、Trie树(字典树/前缀树)Java实现
4、Trie 树实现搜索引擎自动联想