Trie 树
树(又叫「前缀树」或「字典树」)是一种用于快速查询「某个字符串/字符前缀」是否存在的数据结构。
其核心是使用「边」来代表有无字符,使用「点」来记录是否为「单词结尾」以及「其后续字符串的字符是什么」。
数组实现 Trie
一个朴素的想法是直接使用「二维数组」来实现 树。
由于题目要求我们实现删除操作,因此我们需要额外开一个 cnt
数组用于记录某个字符出现次数。
与此同时,我们可以设计一个同时支持 写入/删除 的函数 insert
:
- 写入
s
时,调用insert(s, 1)
; - 删除
s
时,调用insert(s, -1)
。
综上,数组实现 我们一共需要如下属性:
- 使用二维数组 来存储我们所有的单词字符;
- 使用 来自增记录我们到底用了多少个格子(相当于给被用到格子进行编号);
- 使用 数组记录某个格子被「被标记的次数」(当 编号的格子被标记了 次,则有 );
- 使用 数组记录记录某个格子被「被标记为结尾的次数」。
代码:
import java.util.*; public class Solution { class Trie { int N = 1000009; int[][] tr = new int[N][26]; int[] cnt = new int[N]; int[] isWord = new int[N]; int idx = 0; void insert(String s, int v) { int p = 0; for (int i = 0; i < s.length(); i++) { int u = s.charAt(i) - 'a'; if (tr[p][u] == 0) tr[p][u] = ++idx; p = tr[p][u]; cnt[p] += v; } isWord[p] += v; } boolean search(String s) { int p = 0; for (int i = 0; i < s.length(); i++) { int u = s.charAt(i) - 'a'; if (cnt[tr[p][u]] == 0) return false; p = tr[p][u]; } return isWord[p] > 0; } int prefixNumber(String s) { int p = 0; for (int i = 0; i < s.length(); i++) { int u = s.charAt(i) - 'a'; if (cnt[tr[p][u]] == 0) return 0; p = tr[p][u]; } return cnt[p]; } } Trie trie = new Trie(); public String[] trieU (String[][] operators) { List<String> list = new ArrayList<>(); for (String[] cur : operators) { if (cur[0].equals("1")) { trie.insert(cur[1], 1); } else if (cur[0].equals("2")) { trie.insert(cur[1], -1); } else if (cur[0].equals("3")) { list.add(trie.search(cur[1]) ? "YES" : "NO"); } else if (cur[0].equals("4")) { list.add(String.valueOf(trie.prefixNumber(cur[1]))); } } int m = list.size(); String[] ans = new String[m]; for (int i = 0; i < m; i++) { ans[i] = list.get(i); } return ans; } }
最后
这是我们「必考真题 の 精选」系列文章的第 No.124
篇,系列开始于 2021/07/01。
该系列会将牛客网中「题霸 - 面试必考真题」中比较经典而又不过时的题目都讲一遍。
在提供追求「证明」&「思路」的同时,提供最为简洁的代码。
欢迎关注,交个朋友 (`・ω・´)