问题引入 一个字符串类型的数组arr1,另一个字符串类型的数组arr2,arr2中有哪些字符,是arr1中出现的?请打印。arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印。arr2中有哪些字符,是作为arr1中某个字符前缀出现的?请打印arr2中出现次数最大的前缀。
前缀树
前缀树介绍
这棵Trie树包含的字符串集合是{in, inn, int, tea, ten, to}。每个节点的编号是我们为了描述方便加上去的。树中的每一条边上都标识有一个字符。
比如上图中3号节点对应的路径0123上的字符串是inn,8号节点对应的路径0568上的字符串是ten。终结点与集合中的字符串是一一对应的。
前缀树操作
Trie树的创建是从只有根节点开始,通过依次将W1, W2, W3, … WN插入Trie中实现的 Trie一般支持两个操作:
Trie.insert(W):第一个操作是插入操作,就是将一个字符串W加入到集合中。
Trie.search(S):第二个操作是查询操作,就是查询一个字符串S是不是在集合中。
插入操作原理
插入字符串in。开始位于根,用P=0表示。先看P是不是有一条标识着i的连向子节点的边。没有这条边就新建一个1号节点,把1号节点设置为P,并且将边标识为i。最后我们移动到1号节点,令P=1。
插入字符n,先找P有没有标记为n的边。还是没有,于是再新建一个节点2,并且把边标识为n。最后P=2。由于n是in的最后一个字符,所以我们还需要将P=2这个节点标记为终结点
再插入字符串inn。从P=0开始找标识为i的边,这次找到1号节点。直接移动到1号节点,也就是令P=1。再插入字符n,也是有2号节点存在,所以移动到2号节点,P=2。最后再插入字符n,这时P没有标识为n的边了,所以新建3号节点作为2号节点的子节点,边标识为n,同时将3号节点标记为终结点:
将后面的字符串int tea ten to都插入之后,就得到了我们一开始给出的Trie
插入操作伪代码
1 2 3 4 5 6 7 8 9 10 11 Initialize: cur = root for each char c in target string S: if cur does not have a child c: cur.children[c] = new Trie node cur = cur.children[c] cur is the node which represents the string S
搜索操作伪代码
1 2 3 4 5 6 7 8 9 10 Initialize: cur = root for each char c in target string S: if cur does not have a child c: search fails cur = cur.children[c] search successes
解决方案 对于问题引入部分的题目和给出的算法示意图给出以下结构:
前缀树节点结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class TrieNode { public int path; public int end; public TrieNode[] children; public TrieNode () { path = 0 ; end = 0 ; children = new TrieNode [26 ]; } }
前缀树操作
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 public class Trie { private TrieNode root; public Trie () { this .root = new TrieNode (); } public void insert (String word) { if (word == null ) { return ; } char [] chs = word.toCharArray(); TrieNode node = root; int index = 0 ; for (int i = 0 ; i < chs.length; i++) { index = chs[i] - 'a' ; if (node.children[index] == null ) { node.children[index] = new TrieNode (); } node = node.children[index]; node.path++; } node.end++; } public void delete (String word) { if (search(word) != 0 ) { char [] chs = word.toCharArray(); TrieNode node = root; int index = 0 ; for (int i = 0 ; i < chs.length; i++) { index = chs[i] - 'a' ; if (--node.children[index].path == 0 ) { node.children[index] = null ; return ; } node = node.children[index]; } node.end--; } } public int search (String word) { if (word == null ) { return 0 ; } char [] chs = word.toCharArray(); TrieNode node = root; int index = 0 ; for (int i = 0 ; i < chs.length; i++) { index = chs[i] - 'a' ; if (node.children[index] == null ) { return 0 ; } node = node.children[index]; } return node.end; } public int prefixNumber (String pre) { if (pre == null ) { return 0 ; } char [] chs = pre.toCharArray(); TrieNode node = root; int index = 0 ; for (int i = 0 ; i < chs.length; i++) { index = chs[i] - 'a' ; if (node.children[index] == null ) { return 0 ; } node = node.children[index]; } return node.path; } }
总结 [1/4]
[2/4]
[3/4]
[4/4]