Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 64afc95

Browse files
✨feat: add 648
1 parent 8279ba0 commit 64afc95

File tree

5 files changed

+176
-7
lines changed

5 files changed

+176
-7
lines changed

‎Index/字典树.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
| [211. 添加与搜索单词 - 数据结构设计](https://leetcode-cn.com/problems/design-add-and-search-words-data-structure/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/design-add-and-search-words-data-structure/solution/gong-shui-san-xie-yi-ti-shuang-jie-er-we-un94/) | 中等 | 🤩🤩🤩🤩🤩 |
55
| [212. 单词搜索 II](https://leetcode-cn.com/problems/word-search-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/word-search-ii/solution/gong-shui-san-xie-yi-ti-shuang-jie-hui-s-am8f/) | 困难 | 🤩🤩🤩🤩 |
66
| [421. 数组中两个数的最大异或值](https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-bmjdg/) | 中等 | 🤩🤩🤩🤩 |
7+
| [648. 单词替换](https://leetcode.cn/problems/replace-words/) | [LeetCode 题解链接](https://leetcode.cn/problems/replace-words/solution/by-ac_oier-jecf/) | 中等 | 🤩🤩🤩🤩🤩 |
78
| [677. 键值映射](https://leetcode-cn.com/problems/map-sum-pairs/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/map-sum-pairs/solution/gong-shui-san-xie-jie-he-dfs-de-trie-yun-i4xa/) | 中等 | 🤩🤩🤩🤩 |
89
| [720. 词典中最长的单词](https://leetcode-cn.com/problems/longest-word-in-dictionary/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-word-in-dictionary/solution/by-ac_oier-bmot/) | 简单 | 🤩🤩🤩🤩 |
910
| [1707. 与数组中元素的最大异或值](https://leetcode-cn.com/problems/maximum-xor-with-an-element-from-array/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-xor-with-an-element-from-array/solution/gong-shui-san-xie-jie-zhe-ge-wen-ti-lai-lypqr/) | 困难 | 🤩🤩🤩 |

‎LeetCode/1701-1710/1707. 与数组中元素的最大异或值(困难).md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
这是 LeetCode 上的 **[1707. 与数组中元素的最大异或值](https://leetcode-cn.com/problems/maximum-xor-with-an-element-from-array/solution/gong-shui-san-xie-jie-zhe-ge-wen-ti-lai-lypqr/)** ,难度为 **困难**
44

5-
Tag : 「Trie」、「字典树」、「二分」
5+
Tag :「字典树」、「二分」
66

77

88

‎LeetCode/201-210/208. 实现 Trie (前缀树)(中等).md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
这是 LeetCode 上的 **[208. 实现 Trie (前缀树)](https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/gong-shui-san-xie-yi-ti-shuang-jie-er-we-esm9/)** ,难度为 **中等**
44

5-
Tag : 「Trie」、「字典树」
5+
Tag :「字典树」
66

77

88

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[648. 单词替换](https://leetcode.cn/problems/replace-words/solution/by-ac_oier-jecf/)** ,难度为 **中等**
4+
5+
Tag : 「字典树」
6+
7+
8+
9+
在英语中,我们有一个叫做 词根(`root`) 的概念,可以词根后面添加其他一些词组成另一个较长的单词——我们称这个词为 继承词(`successor`)。例如,词根`an`,跟随着单词 `other`(其他),可以形成新的单词 `another`(另一个)。
10+
11+
现在,给定一个由许多词根组成的词典 `dictionary` 和一个用空格分隔单词形成的句子 `sentence`。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
12+
13+
你需要输出替换之后的句子。
14+
15+
示例 1:
16+
```
17+
输入:dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
18+
19+
输出:"the cat was rat by the bat"
20+
```
21+
示例 2:
22+
```
23+
输入:dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs"
24+
25+
输出:"a a b c"
26+
```
27+
28+
提示:
29+
* 1ドル <= dictionary.length <= 1000$
30+
* 1ドル <= dictionary[i].length <= 100$
31+
* `dictionary[i]` 仅由小写字母组成。
32+
* 1ドル <= sentence.length <= 10^6$
33+
* `sentence` 仅由小写字母和空格组成。
34+
* `sentence` 中单词的总量在范围 $[1, 1000]$ 内。
35+
* `sentence` 中每个单词的长度在范围 $[1, 1000]$ 内。
36+
* `sentence` 中单词之间由一个空格隔开。
37+
* `sentence` 没有前导或尾随空格。
38+
39+
---
40+
41+
### 基本分析
42+
43+
这是一道 `Trie` 的模板题,还不了解 `Trie` 的同学可以先看前置 🧀:[【设计数据结构】实现 Trie (前缀树)
44+
](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247488490&idx=1&sn=db2998cb0e5f08684ee1b6009b974089)
45+
46+
前置 🧀 通过图解形式讲解了 `Trie` 的结构与原理,以及提供了两种实现 `Trie` 的方式。
47+
48+
回到本题,为了方便,我们令 `ds``dictionary`,令 `s``sentence`
49+
50+
---
51+
52+
### 二维数组
53+
54+
一个比较习惯的做法,是使用「二维数组」来实现 `Trie`,配合 `static` 优化,可以有效控制 `new` 的次数,耗时相对稳定。
55+
56+
考虑两个 `Trie` 的基本操作:
57+
58+
* `add` 操作:变量入参字符串 `s`,将字符串中的每位字符映射到 $[0, 25],ドル同时为了能够方便查询某个字符串(而不只是某个前缀)是否曾经存入过 `Trie` 中,额外使用一个布尔数组 `isEnd` 记录某个位置是否为单词结尾。
59+
* `query` 操作:
60+
61+
至于二维数组的大小估算,可以直接开成 $N \times C,ドル其中 $N$ 为要插入到 `Trie` 中的字符总数,$C$ 为对应的字符集大小。在 $N \times C$ 没有 `MLE` 风险时,可以直接开这么多;而当 $N \times C$ 较大(超过 1ドルe7,ドル甚至 1ドルe8$ 时),可以适当将 $N \times C$ 中的 $N$ 减少,使得总空间在 1ドルe7$ 左右,因为实际上由于二维数组中的某些行中会存储一个字符以上,实际上我们用不到这么多行。
62+
63+
代码(不使用 `static` 优化,耗时增加十倍):
64+
```Java
65+
class Solution {
66+
static int N = 100000, M = 26;
67+
static int[][] tr = new int[N][M];
68+
static boolean[] isEnd = new boolean[N * M];
69+
static int idx;
70+
void add(String s) {
71+
int p = 0;
72+
for (int i = 0; i < s.length(); i++) {
73+
int u = s.charAt(i) - 'a';
74+
if (tr[p][u] == 0) tr[p][u] = ++idx;
75+
p = tr[p][u];
76+
}
77+
isEnd[p] = true;
78+
}
79+
String query(String s) {
80+
for (int i = 0, p = 0; i < s.length(); i++) {
81+
int u = s.charAt(i) - 'a';
82+
if (tr[p][u] == 0) break;
83+
if (isEnd[tr[p][u]]) return s.substring(0, i + 1);
84+
p = tr[p][u];
85+
}
86+
return s;
87+
}
88+
public String replaceWords(List<String> ds, String s) {
89+
for (int i = 0; i <= idx; i++) {
90+
Arrays.fill(tr[i], 0);
91+
isEnd[i] = false;
92+
}
93+
for (String d : ds) add(d);
94+
StringBuilder sb = new StringBuilder();
95+
for (String str : s.split(" ")) sb.append(query(str)).append(" ");
96+
return sb.substring(0, sb.length() - 1);
97+
}
98+
}
99+
```
100+
* 时间复杂度:令 $n = \sum_{i = 0}^{ds.length - 1} ds[i].length,ドル$m$ 为 `s` 长度,复杂度为 $O(n + m)$
101+
* 空间复杂度:$O(n \times C),ドル其中 $C = 26$ 为字符集大小
102+
103+
---
104+
105+
### TrieNode
106+
107+
另外一个能够有效避免「估数组大小」操作的方式,是使用 `TrieNode` 的方式实现 `Trie`:每次使用到新的格子再触发 `new` 操作。
108+
109+
至于为什么有了 `TrieNode` 的方式,我还是会采用「二维数组」优先的做法,在 [知乎](https://zhuanlan.zhihu.com/p/531180364) 上有同学问过我类似的问题,只不过原问题是「为什么有了动态开点线段树,直接 `build` 出 4ドルn$ 空间的做法仍有意义」,这对应到本题使用「二维数组」还是「TrieNode」是一样的道理:
110+
111+
除非某些语言在启动时,采用虚拟机的方式,并且预先分配了足够的内存,否则所有的 `new` 操作都需要反映到 os 上,而在 `linux` 分配时需要遍历红黑树,因此即使是总空间一样,一次性的 `new` 要比多次小空间的 `new` 更省时间,同时集中性的 `new` 也比分散性的 `new` 操作要更快,这也就是为什么我们不无脑使用 `TrieNode` 的原因。
112+
113+
代码:
114+
```Java
115+
class Solution {
116+
class Node {
117+
boolean isEnd;
118+
Node[] tns = new Node[26];
119+
}
120+
Node root = new Node();
121+
void add(String s) {
122+
Node p = root;
123+
for (int i = 0; i < s.length(); i++) {
124+
int u = s.charAt(i) - 'a';
125+
if (p.tns[u] == null) p.tns[u] = new Node();
126+
p = p.tns[u];
127+
}
128+
p.isEnd = true;
129+
}
130+
String query(String s) {
131+
Node p = root;
132+
for (int i = 0; i < s.length(); i++) {
133+
int u = s.charAt(i) - 'a';
134+
if (p.tns[u] == null) break;
135+
if (p.tns[u].isEnd) return s.substring(0, i + 1);
136+
p = p.tns[u];
137+
}
138+
return s;
139+
}
140+
public String replaceWords(List<String> ds, String s) {
141+
for (String str : ds) add(str);
142+
StringBuilder sb = new StringBuilder();
143+
for (String str : s.split(" ")) sb.append(query(str)).append(" ");
144+
return sb.substring(0, sb.length() - 1);
145+
}
146+
}
147+
```
148+
* 时间复杂度:令 $n = \sum_{i = 0}^{ds.length - 1} ds[i].length,ドル$m$ 为 `s` 长度,复杂度为 $O(n + m)$
149+
* 空间复杂度:$O(n \times C),ドル其中 $C = 26$ 为字符集大小
150+
151+
---
152+
153+
### 加餐
154+
155+
**今天额外增加一道更贴合笔试面试的加餐题 : [结合 DFS 的 Trie 运用题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492188&idx=1&sn=a1436d1ffe2b8200a36c3196ca1c7ed1) 🎉🎉**
156+
157+
---
158+
159+
### 最后
160+
161+
这是我们「刷穿 LeetCode」系列文章的第 `No.648` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
162+
163+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
164+
165+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
166+
167+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
168+

‎LeetCode/671-680/677. 键值映射(中等).md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Tag : 「字典树」、「DFS」、「哈希表」
66

77

88

9-
实现一个 MapSum 类,支持两个方法,insert 和 sum:
9+
实现一个 `MapSum` 类,支持两个方法,`insert``sum`:
1010

1111
* `MapSum()` 初始化 `MapSum` 对象
1212
* `void insert(String key, int val)` 插入 `key-val` 键值对,字符串表示键 `key` ,整数表示值 `val` 。如果键 `key` 已经存在,那么原来的键值对将被替代成新的键值对。
@@ -30,7 +30,7 @@ mapSum.sum("ap"); // return 5 (apple + app = 3 + 2 = 5)
3030
```
3131

3232
提示:
33-
* `1 <= key.length, prefix.length <= 50`
33+
* $1 <= key.length, prefix.length <= 50$
3434
* `key``prefix` 仅由小写英文字母组成
3535
* `1 <= val <= 1000`
3636
* 最多调用 `50``insert``sum`
@@ -119,8 +119,8 @@ class MapSum {
119119
}
120120
}
121121
```
122-
* 时间复杂度:令 $key$ 的最大长度为 $n,ドル最大调用次数为 $m,ドル字符集大小为 $C$( 本题 $C$ 固定为 26ドル$ ),`insert` 操作的复杂度为 $O(n)$;从 `DFS` 的角度分析,`sum` 操作的复杂度为 $O(C^n),ドル但事实上,对于本题具有明确的计算量上界,搜索所有的格子的复杂度为 $O(n * m * C)$
123-
* 空间复杂度:$O(n * m * C)$
122+
* 时间复杂度:令 $key$ 的最大长度为 $n,ドル最大调用次数为 $m,ドル字符集大小为 $C$( 本题 $C$ 固定为 26ドル$ ),`insert` 操作的复杂度为 $O(n)$;从 `DFS` 的角度分析,`sum` 操作的复杂度为 $O(C^n),ドル但事实上,对于本题具有明确的计算量上界,搜索所有的格子的复杂度为 $O(n \times m \times C)$
123+
* 空间复杂度:$O(n \times m \times C)$
124124

125125
---
126126

@@ -196,7 +196,7 @@ class MapSum {
196196
}
197197
```
198198
* 时间复杂度:令 $key$ 的最大长度为 $n,ドル`insert` 操作的复杂度为 $O(n)$;`sum` 操作的复杂度为 $O(n)$
199-
* 空间复杂度:令 $key$ 的最大长度为 $n,ドル最大调用次数为 $m,ドル字符集大小为 $C$( 本题 $C$ 固定为 26ドル$ ),复杂度为 $O(n * m * C)$
199+
* 空间复杂度:令 $key$ 的最大长度为 $n,ドル最大调用次数为 $m,ドル字符集大小为 $C$( 本题 $C$ 固定为 26ドル$ ),复杂度为 $O(n \times m \times C)$
200200

201201
---
202202

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /