【LeetCode】﹝前缀树ி﹞最长单词、键值映射、最大异或值

布满荆棘的人生 2022-10-05 13:46 244阅读 0赞

【LeetCode】﹝前缀树ி﹞最长单词、键值映射、最大异或值

文章目录

    • 【LeetCode】﹝前缀树ி﹞最长单词、键值映射、最大异或值
      • 实现 Trie (前缀树)★★
      • 词典中最长的单词★
      • 键值映射★★
      • 数组中两个数的最大异或值★★
      • 与数组中元素的最大异或值★★★
      • 统计异或值在范围内的数对有多少★★★

前缀树的详细讲解和相关实现可参考往期博客高级数据结构(Ⅴ)单词查找树(Trie)

实现 Trie (前缀树)★★

208. 实现 Trie (前缀树)

题目Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

提示:

  • 1 <= word.length, prefix.length <= 2000
  • wordprefix 仅由小写英文字母组成
  • insertsearchstartsWith 调用次数 总计 不超过 3 * 10^4

示例

  1. 输入
  2. ["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
  3. [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
  4. 输出
  5. [null, null, true, false, true, null, true]
  6. 解释
  7. Trie trie = new Trie();
  8. trie.insert("apple");
  9. trie.search("apple"); // 返回 True
  10. trie.search("app"); // 返回 False
  11. trie.startsWith("app"); // 返回 True
  12. trie.insert("app");
  13. trie.search("app"); // 返回 True

解题思路

  1. class Trie {
  2. class Node{
  3. boolean isEnd;
  4. Node[] next;
  5. Node () {
  6. isEnd = false;
  7. next = new Node[26];
  8. }
  9. }
  10. Node root;
  11. /** Initialize your data structure here. */
  12. public Trie() {
  13. this.root = new Node();
  14. }
  15. /** Inserts a word into the trie. */
  16. public void insert(String word) {
  17. Node p = root;
  18. for (char c : word.toCharArray()) {
  19. if (p.next[c - 'a'] == null) {
  20. p.next[c - 'a'] = new Node();
  21. }
  22. p = p.next[c - 'a'];
  23. }
  24. p.isEnd = true;
  25. }
  26. /** Returns if the word is in the trie. */
  27. public boolean search(String word) {
  28. Node p = root;
  29. for (char c : word.toCharArray()) {
  30. if (p.next[c - 'a'] == null) {
  31. return false;
  32. } else {
  33. p = p.next[c - 'a'];
  34. }
  35. }
  36. return p.isEnd;
  37. }
  38. /** Returns if there is any word in the trie that starts with the given prefix. */
  39. public boolean startsWith(String prefix) {
  40. Node p = root;
  41. for (char c : prefix.toCharArray()) {
  42. if (p.next[c - 'a'] == null) {
  43. return false;
  44. } else {
  45. p = p.next[c - 'a'];
  46. }
  47. }
  48. return true;
  49. }
  50. }
  51. /**
  52. * Your Trie object will be instantiated and called as such:
  53. * Trie obj = new Trie();
  54. * obj.insert(word);
  55. * boolean param_2 = obj.search(word);
  56. * boolean param_3 = obj.startsWith(prefix);
  57. */

词典中最长的单词★

720. 词典中最长的单词

题目】给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。

若无答案,则返回空字符串。

提示:

  • 所有输入的字符串都只包含小写字母。
  • words数组长度范围为[1,1000]
  • words[i]的长度范围为[1,30]

示例

  1. 输入:
  2. words = ["w","wo","wor","worl", "world"]
  3. 输出:"world"
  4. 解释:
  5. 单词"world"可由"w", "wo", "wor", "worl"添加一个字母组成。

解题思路

方法一:排序+Set集合

  1. class Solution {
  2. public String longestWord(String[] words) {
  3. Set<String> set = new HashSet<>();
  4. String res = new String();
  5. Arrays.sort(words);
  6. for (String str : words) {
  7. if (str.length() == 1 || set.contains(str.substring(0, str.length() - 1))) {
  8. res = str.length() > res.length() ? str : res;
  9. set.add(str);
  10. }
  11. }
  12. return res;
  13. }
  14. }

方法二:字典树

  1. class Solution {
  2. class Node {
  3. boolean isEnd;
  4. Node[] next;
  5. String word;
  6. Node() {
  7. isEnd = false;
  8. next = new Node[26];
  9. word = null;
  10. }
  11. }
  12. Node root;
  13. String res;
  14. int maxDepth;
  15. public String longestWord(String[] words) {
  16. root = new Node();
  17. for (String word : words) {
  18. insert(word);
  19. }
  20. res = "";
  21. maxDepth = 0;
  22. dfs(root, 0);
  23. return res;
  24. }
  25. private void insert(String str) {
  26. Node p = root;
  27. for (char c : str.toCharArray()) {
  28. if (p.next[c - 'a'] == null) {
  29. p.next[c - 'a'] = new Node();
  30. }
  31. p = p.next[c - 'a'];
  32. }
  33. p.isEnd = true;
  34. p.word = str;
  35. }
  36. private void dfs(Node p, int depth) {
  37. if (depth > 0 && !p.isEnd) {
  38. return;
  39. }
  40. if (depth > maxDepth) {
  41. maxDepth = depth;
  42. res = p.word;
  43. }
  44. for (Node cur : p.next) {
  45. if (cur != null) {
  46. dfs(cur, depth + 1);
  47. }
  48. }
  49. }
  50. }

键值映射★★

677. 键值映射

题目】实现一个 MapSum 类,支持两个方法,insertsum

MapSum() 初始化 MapSum 对象
void insert(String key, int val) 插入 key-val 键值对,字符串表示键 key ,整数表示值 val 。如果键 key 已经存在,那么原来的键值对将被替代成新的键值对。
int sum(string prefix) 返回所有以该前缀 prefix 开头的键 key 的值的总和。

提示

  • 1 <= key.length, prefix.length <= 50
  • keyprefix 仅由小写英文字母组成
  • 1 <= val <= 1000
  • 最多调用 50insertsum

示例

  1. 输入:
  2. ["MapSum", "insert", "sum", "insert", "sum"]
  3. [[], ["apple", 3], ["ap"], ["app", 2], ["ap"]]
  4. 输出:
  5. [null, null, 3, null, 5]
  6. 解释:
  7. MapSum mapSum = new MapSum();
  8. mapSum.insert("apple", 3);
  9. mapSum.sum("ap"); // return 3 (apple = 3)
  10. mapSum.insert("app", 2);
  11. mapSum.sum("ap"); // return 5 (apple + app = 3 + 2 = 5)

解题思路

  1. class MapSum {
  2. class Node{
  3. int val;
  4. Node[] next;
  5. Node() {
  6. this.val = 0;
  7. this.next = new Node[26];
  8. }
  9. }
  10. Node root;
  11. /** Initialize your data structure here. */
  12. public MapSum() {
  13. root = new Node();
  14. }
  15. public void insert(String key, int val) {
  16. Node p = root;
  17. for (char c : key.toCharArray()) {
  18. if (p.next[c - 'a'] == null) {
  19. p.next[c - 'a'] = new Node();
  20. }
  21. p = p.next[c - 'a'];
  22. }
  23. p.val = val;
  24. }
  25. public int sum(String prefix) {
  26. Node p = root;
  27. int sum = 0, i = 0;
  28. while (i < prefix.length()) {
  29. char c = prefix.charAt(i);
  30. if (p.next[c - 'a'] == null) {
  31. break;
  32. }
  33. p = p.next[c - 'a'];
  34. i++;
  35. }
  36. if (i == prefix.length()) {
  37. sum = getSum(p);
  38. }
  39. return sum;
  40. }
  41. private int getSum(Node p) {
  42. int cur = p.val;
  43. for (int i = 0; i < 26; i++) {
  44. if (p.next[i] != null) {
  45. cur += getSum(p.next[i]);
  46. }
  47. }
  48. return cur;
  49. }
  50. }

数组中两个数的最大异或值★★

421. 数组中两个数的最大异或值

题目】给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n

进阶:你可以在 O(n) 的时间解决这个问题吗?

提示:

  • 1 <= nums.length <= 2 * 104
  • 0 <= nums[i] <= 231 - 1

示例

  1. 输入:nums = [3,10,5,25,2,8]
  2. 输出:28
  3. 解释:最大运算结果是 5 XOR 25 = 28.

解题思路

异或相同为0,不同为1,在01字典树中取二进制位不同的方向搜索,这样才能使得到的结果最大。

  • 若二进制位不同的位置字典树不为空,向此方向搜索
  • 否则,继续向相同的位置往下搜索

采取的策略是边加入边搜索,防止重复判断。

  1. class Solution {
  2. class Node{
  3. Node[] ns = new Node[2];
  4. }
  5. Node root;
  6. private void put(int num) {
  7. Node p = root;
  8. for (int i = 31; i >= 0; i--) {
  9. int t = (num >> i) & 1;
  10. if (p.ns[t] == null) {
  11. p.ns[t] = new Node();
  12. }
  13. p = p.ns[t];
  14. }
  15. }
  16. private int get(int num) {
  17. int val = 0;
  18. Node p = root;
  19. for (int i = 31; i >= 0; i--) {
  20. int a = (num >> i) & 1, b = 1 - a;
  21. if (p.ns[b] != null) {
  22. val |= (b << i);
  23. p = p.ns[b];
  24. } else {
  25. val |= (a << i);
  26. p = p.ns[a];
  27. }
  28. }
  29. return val;
  30. }
  31. public int findMaximumXOR(int[] nums) {
  32. root = new Node();
  33. int res = 0;
  34. for (int num : nums) {
  35. put(num);
  36. int t = get(num);
  37. res = Math.max(res, num ^ t);
  38. }
  39. return res;
  40. }
  41. }

与数组中元素的最大异或值★★★

1707. 与数组中元素的最大异或值

题目】给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi]

i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1

返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.lengthanswer[i] 是第 i 个查询的答案。

提示

  • 1 <= nums.length, queries.length <= 105
  • queries[i].length == 2
  • 0 <= nums[j], xi, mi <= 109

示例

  1. 输入:nums = [0,1,2,3,4], queries = [[3,1],[1,3],[5,6]]
  2. 输出:[3,3,7]
  3. 解释:
  4. 1) 0 1 是仅有的两个不超过 1 的整数。0 XOR 3 = 3 1 XOR 3 = 2 。二者中的更大值是 3
  5. 2) 1 XOR 2 = 3.
  6. 3) 5 XOR 2 = 7.

解题思路

离线模式查询+字典树

  1. class Solution {
  2. private class Node{
  3. Node[] ns = new Node[2];
  4. }
  5. Node root;
  6. private void insert(int v) {
  7. Node p = root;
  8. for (int i = 29; i >= 0; i--) {
  9. int t = (v >> i) & 1;
  10. if (p.ns[t] == null) {
  11. p.ns[t] = new Node();
  12. }
  13. p = p.ns[t];
  14. }
  15. }
  16. private int query(int v) {
  17. Node p = root;
  18. int val = 0;
  19. for (int i = 29; i >= 0; i--) {
  20. int t = (v >> i) & 1;
  21. if (p.ns[t ^ 1] != null) {
  22. val |= 1 << i;
  23. t ^= 1;
  24. }
  25. p = p.ns[t];
  26. }
  27. return val;
  28. }
  29. public int[] maximizeXor(int[] nums, int[][] queries) {
  30. root = new Node();
  31. Arrays.sort(nums);
  32. int n = queries.length;
  33. int[][] que = new int[n][3];
  34. for (int i = 0; i < n; i++) {
  35. que[i][0] = queries[i][0];
  36. que[i][1] = queries[i][1];
  37. que[i][2] = i;
  38. }
  39. Arrays.sort(que, (a, b) -> {
  40. return a[1] - b[1];
  41. });
  42. int[] res = new int[n];
  43. int k = 0;
  44. for (int[] p : que) {
  45. while (k < nums.length && nums[k] <= p[1]) {
  46. insert(nums[k++]);
  47. }
  48. if (k == 0) {
  49. res[p[2]] = -1;
  50. } else {
  51. res[p[2]] = query(p[0]);
  52. }
  53. }
  54. return res;
  55. }
  56. }

在线模式查询+字典树+最小值

  1. class Solution {
  2. class Node{
  3. int min = Integer.MAX_VALUE;
  4. Node[] ns = new Node[2];
  5. }
  6. Node root;
  7. private void insert(int x) {
  8. Node p = root;
  9. p.min = Math.min(p.min, x);
  10. for (int i = 29; i >= 0; i--) {
  11. int t = (x >> i) & 1;
  12. if (p.ns[t] == null) {
  13. p.ns[t] = new Node();
  14. }
  15. p = p.ns[t];
  16. p.min = Math.min(p.min, x);
  17. }
  18. }
  19. private int queryMaxXor(int x, int h) {
  20. Node p = root;
  21. if (p.min > h) {
  22. return -1;
  23. }
  24. int res = 0;
  25. for (int i = 29; i >= 0; i--) {
  26. int t = (x >> i) & 1;
  27. if (p.ns[t ^ 1] != null && p.ns[t ^ 1].min <= h) {
  28. res |= 1 << i;
  29. t ^= 1;
  30. }
  31. p = p.ns[t];
  32. }
  33. return res;
  34. }
  35. public int[] maximizeXor(int[] nums, int[][] queries) {
  36. root = new Node();
  37. for (int num : nums) {
  38. insert(num);
  39. }
  40. int m = queries.length;
  41. int[] ans = new int[m];
  42. for (int i = 0; i < m; i++) {
  43. ans[i] = queryMaxXor(queries[i][0], queries[i][1]);
  44. }
  45. return ans;
  46. }
  47. }

统计异或值在范围内的数对有多少★★★

1803. 统计异或值在范围内的数对有多少

题目】给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:lowhigh ,请返回 漂亮数对 的数目。

漂亮数对 是一个形如 (i, j) 的数对,其中 0 <= i < j < nums.lengthlow <= (nums[i] XOR nums[j]) <= high 。

提示

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i] <= 2 * 104
  • 1 <= low <= high <= 2 * 104

示例

  1. 输入:nums = [1,4,2,7], low = 2, high = 6
  2. 输出:6
  3. 解释:所有漂亮数对 (i, j) 列出如下:
  4. - (0, 1): nums[0] XOR nums[1] = 5
  5. - (0, 2): nums[0] XOR nums[2] = 3
  6. - (0, 3): nums[0] XOR nums[3] = 6
  7. - (1, 2): nums[1] XOR nums[2] = 6
  8. - (1, 3): nums[1] XOR nums[3] = 3
  9. - (2, 3): nums[2] XOR nums[3] = 5

解题思路

字典树+计数

  1. class Solution {
  2. class Node{
  3. int count;
  4. Node[] ns;
  5. Node () {
  6. count = 0;
  7. ns = new Node[2];
  8. }
  9. }
  10. Node root;
  11. private void insert(int x) {
  12. Node p = root;
  13. //注意nums[i] <= 2x10^4 , 所以取最大 i << 15
  14. for (int i = 15; i >= 0; i--) {
  15. int t = (x >> i) & 1;
  16. if (p.ns[t] == null) {
  17. p.ns[t] = new Node();
  18. }
  19. p = p.ns[t];
  20. p.count += 1;
  21. }
  22. }
  23. private int query(int x, int h) {
  24. int sum = 0;
  25. Node p = root;
  26. for (int i = 15; i >= 0; i--) {
  27. if (p == null) break;
  28. int t = (x >> i) & 1;
  29. int ht = (h >> i) & 1;
  30. if (ht == 1) {
  31. //若上限h当前位为1,则与x相同的位的数异或均小于h
  32. if (p.ns[t] != null) {
  33. sum += p.ns[t].count;
  34. }
  35. //走向异或结果为1的位置
  36. p = p.ns[1 - t];
  37. } else {
  38. //走向异或结果为0的位置
  39. p = p.ns[t];
  40. }
  41. }
  42. return sum;
  43. }
  44. public int countPairs(int[] nums, int low, int high) {
  45. root = new Node();
  46. int res = 0;
  47. for (int num : nums) {
  48. res += query(num, high + 1) - query(num, low);
  49. insert(num);
  50. }
  51. return res;
  52. }
  53. }

发表评论

表情:
评论列表 (有 0 条评论,244人围观)

还没有评论,来说两句吧...

相关阅读