Skip to content

Commit

Permalink
更新题解列表
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Nov 4, 2022
1 parent b4ffe82 commit c932855
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 54 deletions.
40 changes: 36 additions & 4 deletions Solutions/0208. 实现 Trie (前缀树).md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## 题目大意

要求:实现前缀树数据结构的相关类 `Trie` 类。
**要求**:实现前缀树数据结构的相关类 `Trie` 类。

`Trie` 类:

Expand All @@ -14,27 +14,54 @@
- `boolean search(String word)` 如果字符串 `word` 在前缀树中,返回 `True`(即,在检索之前已经插入);否则,返回 `False`
- `boolean startsWith(String prefix)` 如果之前已经插入的字符串 `word` 的前缀之一为 `prefix`,返回 `True`;否则,返回 `False`

**说明**

- $1 \le word.length, prefix.length \le 2000$。
- `word``prefix` 仅由小写英文字母组成。
- `insert``search``startsWith` 调用次数 **总计** 不超过 $3 * 10^4$ 次。

**示例**

```Python
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]

解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
```

## 解题思路

### 思路 1:前缀树(字典树)

前缀树(字典树)是一棵多叉树,其中每个节点包含指向子节点的指针数组 `children`,以及布尔变量 `isEnd``children` 用于存储当前字符节点,一般长度为所含字符种类个数,也可以使用哈希表代替指针数组。`isEnd` 用于判断该节点是否为字符串的结尾。

下面依次讲解插入、查找前缀的具体步骤:

插入字符串:
**插入字符串**

- 从根节点开始插入字符串。对于待插入的字符,有两种情况:
- 如果该字符对应的节点存在,则沿着指针移动到子节点,继续处理下一个字符。
- 如果该字符对应的节点不存在,则创建一个新的节点,保存在 `children` 中对应位置上,然后沿着指针移动到子节点,继续处理下一个字符。
- 重复上述步骤,直到最后一个字符,然后将该节点标记为字符串的结尾。

查找前缀:
**查找前缀**

- 从根节点开始查找前缀,对于待查找的字符,有两种情况:
- 如果该字符对应的节点存在,则沿着指针移动到子节点,继续查找下一个字符。
- 如果该字符对应的节点不存在,则说明字典树中不包含该前缀,直接返回空指针。
- 重复上述步骤,直到最后一个字符搜索完毕,则说明字典树中存在该前缀。

## 代码
### 思路 1:代码

```Python
class Node:
Expand Down Expand Up @@ -73,3 +100,8 @@ class Trie:
return cur is not None
```

### 思路 1:复杂度分析

- **时间复杂度**:初始化为 $O(1)$。插入操作、查找操作的时间复杂度为 $O(|S|)$。其中 $|S|$ 是每次插入或查找字符串的长度。
- **空间复杂度**:$O(|T| \times \sum)$。其中 $|T|$ 是所有插入字符串的长度之和,$\sum$ 是字符集的大小。

38 changes: 36 additions & 2 deletions Solutions/0211. 添加与搜索单词 - 数据结构设计.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,45 @@

## 题目大意

要求:设计一个数据结构,支持「添加新单词」和「查找字符串是否与任何先前添加的字符串匹配」。
**要求**:设计一个数据结构,支持「添加新单词」和「查找字符串是否与任何先前添加的字符串匹配」。

实现词典类 WordDictionary:

- `WordDictionary()` 初始化词典对象。
- `void addWord(word)``word` 添加到数据结构中,之后可以对它进行匹配
- `bool search(word)` 如果数据结构中存在字符串与 `word` 匹配,则返回 `True`;否则,返回 `False``word` 中可能包含一些 `.`,每个 `.` 都可以表示任何一个字母。

**说明**

- $1 \le word.length \le 25$。
- `addWord` 中的 `word` 由小写英文字母组成。
- `search` 中的 `word``'.'` 或小写英文字母组成。
- 最多调用 $10^4$ 次 `addWord``search`

**示例**

```Python
输入:
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
输出:
[null,null,null,null,false,true,true,true]

解释:
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // 返回 False
wordDictionary.search("bad"); // 返回 True
wordDictionary.search(".ad"); // 返回 True
wordDictionary.search("b.."); // 返回 True
```

## 解题思路

### 思路 1:字典树

使用前缀树(字典树)。具体做法如下:

- 初始化词典对象时,构造一棵字典树。
Expand All @@ -24,7 +53,7 @@
- 如果遇到其他小写字母,则按 `word` 顺序匹配节点。
- 如果当前节点为 `word` 的结尾,则放回 `True`

## 代码
### 思路 1:代码

```Python
class Trie:
Expand Down Expand Up @@ -88,3 +117,8 @@ class WordDictionary:
return self.trie_tree.search(word)
```

### 思路 1:复杂度分析

- **时间复杂度**:初始化操作为 $O(1)$。添加单词为 $O(|S|)$,搜索单词的平均时间复杂度为 $O(|S|)$,最坏情况下所有字符都是 `'.'`,所以最坏时间复杂度为 $O(|S|^\sum)$。其中 $|S|$ 为单词长度,$\sum$ 为字符集的大小,此处为 $26$。
- **空间复杂度**:$O(|T| * n)$。其中 $|T|$ 为所有添加单词的最大长度,$n$ 为添加字符串个数。

30 changes: 27 additions & 3 deletions Solutions/0459. 重复的子字符串.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,31 @@

## 题目大意

描述:给定一个非空的字符串 `s`
**描述**:给定一个非空的字符串 `s`

要求:检查该字符串 `s` 是否可以通过由它的一个子串重复多次构成。
**要求**:检查该字符串 `s` 是否可以通过由它的一个子串重复多次构成。

**说明**

- $1 \le s.length \le 10^4$。
- `s` 由小写英文字母组成

**示例**

```Python
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。


输入: s = "aba"
输出: false
```

## 解题思路

### 思路 1:KMP 算法

这道题我们可以使用 KMP 算法的 `next` 数组来解决。我们知道 `next[j]` 表示的含义是:**记录下标 `j` 之前(包括 `j`)的模式串 `p` 中,最长相等前后缀的长度。**

而如果整个模式串 `p` 的最长相等前后缀长度不为 `0`,即 `next[len(p) - 1] != 0` ,则说明整个模式串 `p` 中有最长相同的前后缀,假设 `next[len(p) - 1] == k`,则说明 `p[0: k] == p[m - k: m]`。比如字符串 `"abcabcabc"`,最长相同前后缀为 `"abcabc" = "abcabc"`
Expand All @@ -21,7 +40,7 @@
- 我们只需要判断整个子串的长度是否是剩余部分长度的整数倍即可。也就是判断 `len(p) % (len(p) - next[size - 1]) == 0` 是否成立,如果成立,则字符串 `s` 可由 `s[0: len(p) - next[size - 1]]` 构成的子串重复构成,返回 `True`。否则返回 `False`
- 如果最长相等的前后缀是不重叠的,那我们可将重叠部分视为长度为 `0` 的空串,则剩余的部分其实就是去除后缀部分的剩余部分,上述结论依旧成立。 

## 代码
### 思路 1:代码

```Python
class Solution:
Expand Down Expand Up @@ -49,3 +68,8 @@ class Solution:
return False
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(m)$,其中模式串 $p$ 的长度为 $m$。
- **空间复杂度**:$O(m)$。

39 changes: 34 additions & 5 deletions Solutions/0648. 单词替换.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,42 @@

## 题目大意

给定一个由许多词根组成的字典列表 `dictionary`,以及一个句子字符串 `sentence`
**描述**给定一个由许多词根组成的字典列表 `dictionary`,以及一个句子字符串 `sentence`

要求:将句子中有词根的单词用词根替换掉。如果单词有很多词根,则用最短的词根替换掉他。最后输出替换之后的句子。
**要求**:将句子中有词根的单词用词根替换掉。如果单词有很多词根,则用最短的词根替换掉他。最后输出替换之后的句子。

**说明**

- $1 \le dictionary.length \le 1000$。
- $1 \le dictionary[i].length \le 100$。
- `dictionary[i]` 仅由小写字母组成。
- $1 \le sentence.length \le 10^6$。
- `sentence` 仅由小写字母和空格组成。
- `sentence` 中单词的总量在范围 $[1, 1000]$ 内。
- `sentence` 中每个单词的长度在范围 $[1, 1000]$ 内。
- `sentence` 中单词之间由一个空格隔开。
- `sentence` 没有前导或尾随空格。

**示例**

```Python
输入:dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
输出:"the cat was rat by the bat"


输入:dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs"
输出:"a a b c"
```

## 解题思路

将所有的词根存入到前缀树(字典树)中。然后在树上查找每个单词的最短词根。
### 思路 1:字典树

## 代码
1. 构造一棵字典树。
2. 将所有的词根存入到前缀树(字典树)中。
3. 然后在树上查找每个单词的最短词根。

### 思路 1:代码

```Python
class Trie:
Expand Down Expand Up @@ -68,5 +95,7 @@ class Solution:
return ' '.join(words)
```

### 思路 1:复杂度分析


- **时间复杂度**:$O(|dictionary| + |sentence|)$。其中 $|dictionary|$ 是字符串数组 `dictionary` 中的字符总数,$|sentence|$ 是字符串 `sentence` 的字符总数。
- **空间复杂度**:$O(|dictionary| + |sentence|)$。
45 changes: 39 additions & 6 deletions Solutions/0676. 实现一个魔法字典.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,52 @@

## 题目大意

要求:设计一个使用单词表进行初始化的数据结构。单词表中的单词互不相同。如果给出一个单词,要求判定能否将该单词中的一个字母替换成另一个字母,是的所形成的新单词已经在够构建的单词表中。
**要求**:设计一个使用单词表进行初始化的数据结构。单词表中的单词互不相同。如果给出一个单词,要求判定能否将该单词中的一个字母替换成另一个字母,是的所形成的新单词已经在够构建的单词表中。

实现 MagicDictionary 类:

- `MagicDictionary()` 初始化对象。
- `void buildDict(String[] dictionary)` 使用字符串数组 `dictionary` 设定该数据结构,`dictionary` 中的字符串互不相同。
- `bool search(String searchWord)` 给定一个字符串 `searchWord`,判定能否只将字符串中一个字母换成另一个字母,使得所形成的新字符串能够与字典中的任一字符串匹配。如果可以,返回 `True`;否则,返回 `False`

## 解题思路
**说明**

- $1 \le dictionary.length \le 100$。
- $1 \le dictionary[i].length \le 100$。
- `dictionary[i]` 仅由小写英文字母组成。
- `dictionary` 中的所有字符串互不相同。
- $1 \le searchWord.length \le 100$。
- `searchWord` 仅由小写英文字母组成。
- `buildDict` 仅在 `search` 之前调用一次。
- 最多调用 $100$ 次 `search`

**示例**

```Python
输入
["MagicDictionary", "buildDict", "search", "search", "search", "search"]
[[], [["hello", "leetcode"]], ["hello"], ["hhllo"], ["hell"], ["leetcoded"]]
输出
[null, null, false, true, false, false]

解释
MagicDictionary magicDictionary = new MagicDictionary();
magicDictionary.buildDict(["hello", "leetcode"]);
magicDictionary.search("hello"); // 返回 False
magicDictionary.search("hhllo"); // 将第二个 'h' 替换为 'e' 可以匹配 "hello" ,所以返回 True
magicDictionary.search("hell"); // 返回 False
magicDictionary.search("leetcoded"); // 返回 False
```

- 初始化使用字典树结构。
## 解题思路

- `buildDict` 方法中将所有单词存入字典树中。
### 思路 1:字典树

- `search` 方法中替换 `searchWord` 每一个位置上的字符,然后在字典树中查询。
1. 构造一棵字典树。
2. `buildDict` 方法中将所有单词存入字典树中。
3. `search` 方法中替换 `searchWord` 每一个位置上的字符,然后在字典树中查询。

## 代码
### 思路 1:代码

```Python
class Trie:
Expand Down Expand Up @@ -85,3 +114,7 @@ class MagicDictionary:
return False
```

### 思路 1:复杂度分析

- **时间复杂度**:初始化操作是 $O(1)$。构建操作是 $O(|dictionary|)$,搜索操作是 $O(|searchWord| \times |\sum|)$。其中 $|dictionary|$ 是字符串数组 `dictionary` 中的字符个数,$|searchWord|$ 是查询操作中字符串的长度,$|\sum|$ 是字符集的大小。
- **空间复杂度**:$O(|dicitonary|)$。
34 changes: 31 additions & 3 deletions Solutions/0677. 键值映射.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,48 @@

## 题目大意

要求:实现一个 MapSum 类,支持两个方法,`insert``sum`
**要求**:实现一个 MapSum 类,支持两个方法,`insert``sum`

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

**说明**

- $1 \le key.length, prefix.length \le 50$。
- `key``prefix` 仅由小写英文字母组成。
- $1 \le val \le 1000$。
- 最多调用 $50$ 次 `insert``sum`

**示例**

```Python
输入:
["MapSum", "insert", "sum", "insert", "sum"]
[[], ["apple", 3], ["ap"], ["app", 2], ["ap"]]
输出:
[null, null, 3, null, 5]

解释:
MapSum mapSum = new MapSum();
mapSum.insert("apple", 3);
mapSum.sum("ap"); // 返回 3 (apple = 3)
mapSum.insert("app", 2);
mapSum.sum("ap"); // 返回 5 (apple + app = 3 + 2 = 5)
```

## 解题思路

### 思路 1:字典树

可以构造前缀树(字典树)解题。

- 初始化时,构建一棵前缀树(字典树),并增加 `val` 变量。

- 调用插入方法时,用字典树存储 `key`,并在对应字母节点存储对应的 `val`
- 在调用查询总和方法时,先查找该前缀 `prefix` 对应的前缀树节点,从该节点开始,递归遍历该节点的子节点,并累积子节点的 `val`,进行求和,并返回求和累加结果。

## 代码
### 思路 1:代码

```Python
class Trie:
Expand Down Expand Up @@ -85,5 +111,7 @@ class MapSum:
return self.trie_tree.search(prefix)
```

### 思路 1:复杂度分析


- **时间复杂度**`insert` 操作的时间复杂度为 $O(|key|)$。其中 $|key|$ 是每次插入字符串 `key` 的长度。`sum` 操作的时间复杂度是 $O(|prefix|)$,其中 $O(| prefix |)$ 是查询字符串 `prefix` 的长度。
- **空间复杂度**:$O(|T| \times m)$。其中 $|T|$ 表示字符串 `key` 的最大长度,$m$ 表示 `key - val` 的键值数目。
Loading

0 comments on commit c932855

Please sign in to comment.