前言
前缀树是一种利于高效地存储和检索字符串数据集中的键,适合用于表示有基础变量复合出来的复杂变量,如26个字符复合出来的单词,实现快速搜索和插入,以及前缀查询。
一、案例
二、题解
package com.xhu.offer.offerII;
public class Trie {
TreeNode root;
public Trie() {
root = new TreeNode();
}
public void insert(String word) {
char[] chs = word.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
TreeNode n = p.next[idx];
if (n == null) p.next[idx] = new TreeNode();
p = n;
}
p.isEnd = true;
}
public boolean search(String word) {
char[] chs = word.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
TreeNode n = p.next[idx];
if (n == null) return false;
p = n;
}
return p.isEnd;
}
public boolean startsWith(String prefix) {
char[] chs = prefix.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
TreeNode n = p.next[idx];
if (n == null) return false;
p = n;
}
return true;
}
public class TreeNode {
boolean isEnd;
TreeNode[] next;
public TreeNode() {
next = new TreeNode[26];
}
}
}
总结
1)第一次见前缀树结构,适用于固定的单一变量复合出复杂变量问题。
参考文献
[1] LeetCode 原题 [2] LeetCode 用户评论
附录
1、替换单词
package com.xhu.offer.offerII;
import java.util.Iterator;
import java.util.List;
public class ReplaceWords {
TreeNode root = new TreeNode();
public String replaceWords(List<String> dictionary, String sentence) {
buildTrie(dictionary);
StringBuilder sb = new StringBuilder();
String[] strs = sentence.split(" ");
for (String str : strs) {
char[] chs = str.toCharArray();
TreeNode p = root;
int count = 0;
for (char ch : chs) {
int idx = ch - 'a';
TreeNode n = p.next[idx];
if (n == null || p.isEnd) break;
count++;
p = p.next[idx];
}
sb.append(p.isEnd ? str.substring(0, count) : str).append(" ");
}
return sb.toString().trim();
}
private void buildTrie(List<String> dic) {
Iterator<String> iterator = dic.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
char[] chs = s.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
TreeNode n = p.next[idx];
if (n == null) p.next[idx] = new TreeNode();
p = p.next[idx];
}
p.isEnd = true;
}
}
public class TreeNode {
boolean isEnd;
TreeNode[] next = new TreeNode[26];
}
}
2、神奇的字典
package com.xhu.offer.offerII;
import java.util.ArrayList;
import java.util.List;
public class MagicDictionary {
public MagicDictionary() {
}
public void buildDict(String[] dictionary) {
for (String s : dictionary) {
char[] chs = s.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
if (p.next[idx] == null) p.next[idx] = new TreeNode();
p = p.next[idx];
}
p.isEnd = true;
}
}
TreeNode root = new TreeNode();
public boolean search(String searchWord) {
return recursion(root, searchWord, 0, 0);
}
private boolean recursion(TreeNode root, String searchWord, int i, int count) {
if (count > 1 || root == null) return false;
if (i == searchWord.length()) {
if (root.isEnd && count == 1) return true;
else return false;
}
for (int j = 0; j < 26; j++) {
boolean flag = false;
int idx = searchWord.charAt(i) - 'a';
if (idx != j) flag = recursion(root.next[j], searchWord, i + 1, count + 1);
else flag = recursion(root.next[j], searchWord, i + 1, count);
if (flag) return true;
}
return false;
}
public class TreeNode {
boolean isEnd;
TreeNode[] next = new TreeNode[26];
}
}
class MagicDictionary2 {
List<String> cache = new ArrayList<>();
public MagicDictionary2() {
}
public void buildDict(String[] dictionary) {
for (String s : dictionary) cache.add(s);
}
public boolean search(String searchWord) {
int size = cache.size();
for (String s : cache) {
if (s.length() != searchWord.length()) continue;
if (find(s, searchWord)) return true;
}
return false;
}
private boolean find(String s, String searchWord) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != searchWord.charAt(i)) count++;
if (count > 1) return false;
}
return count == 1;
}
}
3、最短单词编码之逆前缀树
掌握好前缀树的各个细节,才能举一反三,采用逆前缀树来解决最短单词编码。
package com.xhu.offer.offerII;
public class MinimumLengthEncoding {
public int minimumLengthEncoding(String[] words) {
buildTree(words);
return countPath(root)[0];
}
private int[] countPath(TreeNode root) {
if (root.next == null) {
return new int[]{1, 1};
}
int res = 0, count = 0;
for (int i = 0; i < 26; i++) {
if (root.next[i] != null) {
int[] r = countPath(root.next[i]);
count += r[1];
res += r[0];
}
}
return new int[]{res + count, count};
}
TreeNode root = new TreeNode();
private void buildTree(String[] words) {
for (String word : words) {
char[] chs = word.toCharArray();
TreeNode p = root;
for (int i = chs.length - 1; i >= 0; i--) {
int idx = chs[i] - 'a';
if (p.next == null) p.next = new TreeNode[26];
if (p.next[idx] == null) p.next[idx] = new TreeNode();
p = p.next[idx];
}
}
}
public class TreeNode {
TreeNode[] next;
}
}
4、单词之和
package com.xhu.offer.offerII;
public class MapSum {
public MapSum() {
}
TreeNode root = new TreeNode();
public void insert(String key, int val) {
char[] chs = key.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
if (p.next == null) p.next = new TreeNode[26];
if (p.next[idx] == null) p.next[idx] = new TreeNode();
p = p.next[idx];
}
p.isEnd = true;
p.val = val;
}
public int sum(String prefix) {
char[] chs = prefix.toCharArray();
TreeNode p = root;
for (char ch : chs) {
int idx = ch - 'a';
if (p.next == null || p.next[idx] == null) return 0;
p = p.next[idx];
}
int[] total = new int[]{0};
order(p, total);
return total[0];
}
private void order(TreeNode p, int[] total) {
if (p.isEnd) {
total[0] += p.val;
}
if (p.next == null) return;
for (int i = 0; i < 26; i++) {
if (p.next[i] != null) order(p.next[i], total);
}
}
public class TreeNode {
boolean isEnd;
int val;
TreeNode[] next;
}
}
5、最大的异或
package com.xhu.offer.offerII;
public class FindMaximumXOR {
TreeNode root = new TreeNode();
public int findMaximumXOR(int[] nums) {
buildTree(nums);
int res = 0;
for (int num : nums) {
int t = find(num);
res = res < t ? t : res;
}
return res;
}
private int find(int num) {
int count = 30;
TreeNode p = root;
int res = 0;
while (count >= 0) {
int m = num >>> count & 1;
boolean f1 = m == 1 && p.right != null, f2 = m == 0 && p.left != null;
if (f1 || f2) res |= 1 << count;
if (p.right != null && (m == 1 || p.left == null)) p = p.right;
else p = p.left;
count--;
}
return res;
}
private void buildTree(int[] nums) {
for (int num : nums) {
int count = 30;
TreeNode p = root;
while (count >= 0) {
int m = num >>> count-- & 1;
boolean f1 = m == 1 && p.left == null, f2 = m == 0 && p.right == null;
if (f1) p.left = new TreeNode();
if (f2) p.right = new TreeNode();
p = m == 1 ? p.left : p.right;
}
}
}
public class TreeNode {
TreeNode left;
TreeNode right;
}
}
|