Skip to content

Commit

Permalink
更新题解列表
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Nov 3, 2022
1 parent 059ab18 commit b4ffe82
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 34 deletions.
137 changes: 130 additions & 7 deletions Solutions/0028. 找出字符串中第一个匹配项的下标.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,30 @@
```Python
输入:haystack = "hello", needle = "ll"
输出:2
解释:"sad" 在下标 06 处匹配。第一个匹配项的下标是 0 ,所以返回 0

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1
```

## 解题思路

字符串匹配的经典题目。常见的字符串匹配算法有:BF(Brute Force)算法、RK(Robin-Karp)算法、KMP(Knuth Morris Pratt)算法、BM(Boyer Moore)算法、Horspool 算法、Sunday 算法等。

### 思路 1:BF(Brute Force)算法代码
### 思路 1:BF(Brute Force)算法

**BF 算法思想**:对于给定文本串 `T` 与模式串 `p`,从文本串的第一个字符开始与模式串 `p` 的第一个字符进行比较,如果相等,则继续逐个比较后续字符,否则从文本串 `T` 的第二个字符起重新和模式串 `p` 进行比较。依次类推,直到模式串 `p` 中每个字符依次与文本串 `T` 的一个连续子串相等,则模式匹配成功。否则模式匹配失败。

BF 算法具体步骤如下:

1. 对于给定的文本串 `T` 与模式串 `p`,求出文本串 `T` 的长度为 `n`,模式串 `p` 的长度为 `m`
2. 同时遍历文本串 `T` 和模式串 `p`,先将 `T[0]``p[0]` 进行比较。
1. 如果相等,则继续比较 `T[1]``p[1]`。以此类推,一直到模式串 `p` 的末尾 `p[m - 1]` 为止。
2. 如果不相等,则将文本串 `T` 移动到上次匹配开始位置的下一个字符位置,模式串 `p` 则回退到开始位置,再依次进行比较。
3. 当遍历完文本串 `T` 或者模式串 `p` 的时候停止搜索。

### 思路 1:代码

```Python
class Solution:
Expand All @@ -50,8 +67,28 @@ class Solution:
return -1
```

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

- **时间复杂度**:平均时间复杂度为 $O(n + m)$,最坏时间复杂度为 $O(m \times n)$。其中文本串 $T$ 的长度为 $n$,模式串 $p$ 的长度为 $m$。
- **空间复杂度**:$O(1)$。

### 思路 2:RK(Robin Karp)算法

**RK 算法思想**:对于给定文本串 `T` 与模式串 `p`,通过滚动哈希算快速筛选出与模式串 `p` 不匹配的文本位置,然后在其余位置继续检查匹配项。

RK 算法具体步骤如下:

1. 对于给定的文本串 `T` 与模式串 `p`,求出文本串 `T` 的长度为 `n`,模式串 `p` 的长度为 `m`
2. 通过滚动哈希算法求出模式串 `p` 的哈希值 `hash_p`
3. 再通过滚动哈希算法对文本串 `T``n - m + 1` 个子串分别求哈希值 `hash_t`
4. 然后逐个与模式串的哈希值比较大小。
1. 如果当前子串的哈希值 `hash_t` 与模式串的哈希值 `hash_p` 不同,则说明两者不匹配,则继续向后匹配。
2. 如果当前子串的哈希值 `hash_t` 与模式串的哈希值 `hash_p` 相等,则验证当前子串和模式串的每个字符是否真的相等(避免哈希冲突)。
1. 如果当前子串和模式串的每个字符相等,则说明当前子串和模式串匹配。
2. 如果当前子串和模式串的每个字符不相等,则说明两者不匹配,继续向后匹配。
5. 比较到末尾,如果仍未成功匹配,则说明文本串 `T` 中不包含模式串 `p`,方法返回 `-1`

### 思路 2:RK(Robin-Karp)算法代码
### 思路 2:代码

```Python
class Solution:
Expand All @@ -75,7 +112,26 @@ class Solution:
return rabinKarp(haystack, needle)
```

### 思路 3:KMP(Knuth Morris Pratt)算法代码
### 思路 1:复杂度分析

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

### 思路 3:KMP(Knuth Morris Pratt)算法

**KMP 算法思想**:对于给定文本串 `T` 与模式串 `p`,当发现文本串 `T` 的某个字符与模式串 `p` 不匹配的时候,可以利用匹配失败后的信息,尽量减少模式串与文本串的匹配次数,避免文本串位置的回退,以达到快速匹配的目的。

KMP 算法具体步骤如下:

1. 根据 `next` 数组的构造步骤生成「前缀表」`next`
2. 使用两个指针 `i``j`,其中 `i` 指向文本串中当前匹配的位置,`j` 指向模式串中当前匹配的位置。初始时,`i = 0``j = 0`
3. 循环判断模式串前缀是否匹配成功,如果模式串前缀匹配不成功,将模式串进行回退,即 `j = next[j - 1]`,直到 `j == 0` 时或前缀匹配成功时停止回退。
4. 如果当前模式串前缀匹配成功,则令模式串向右移动 `1` 位,即 `j += 1`
5. 如果当前模式串 **完全** 匹配成功,则返回模式串 `p` 在文本串 `T` 中的开始位置,即 `i - j + 1`
6. 如果还未完全匹配成功,则令文本串向右移动 `1` 位,即 `i += 1`,然后继续匹配。
7. 如果直到文本串遍历完也未完全匹配成功,则说明匹配失败,返回 `-1`

### 思路 3:代码

```Python
class Solution:
Expand Down Expand Up @@ -120,7 +176,29 @@ class Solution:
return kmp(haystack, needle)
```

### 思路 4:BM(Boyer Moore)算法代码
### 思路 3:复杂度分析

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

### 思路 4:BM(Boyer Moore)算法

**BM 算法思想**:对于给定文本串 `T` 与模式串 `p`,先对模式串 `p` 进行预处理。然后在匹配的过程中,当发现文本串 `T` 的某个字符与模式串 `p` 不匹配的时候,根据启发策略,能够直接尽可能地跳过一些无法匹配的情况,将模式串多向后滑动几位。

BM 算法具体步骤如下:

1. 计算出文本串 `T` 的长度为 `n`,模式串 `p` 的长度为 `m`
2. 先对模式串 `p` 进行预处理,生成坏字符位置表 `bc_table` 和好后缀规则后移位数表 `gs_talbe`
3. 将模式串 `p` 的头部与文本串 `T` 对齐,将 `i` 指向文本串开始位置,即 `i = 0``j` 指向模式串末尾位置,即 `j = m - 1`,然后从模式串末尾位置开始进行逐位比较。
1. 如果文本串对应位置 `T[i + j]` 上的字符与 `p[j]` 相同,则继续比较前一位字符。
1. 如果模式串全部匹配完毕,则返回模式串 `p` 在文本串中的开始位置 `i`
2. 如果文本串对应位置 `T[i + j]` 上的字符与 `p[j]` 不相同,则:
1. 根据坏字符位置表计算出在「坏字符规则」下的移动距离 `bad_move`
2. 根据好后缀规则后移位数表计算出在「好后缀规则」下的移动距离 `good_mode`
3. 取两种移动距离的最大值,然后对模式串进行移动,即 `i += max(bad_move, good_move)`
4. 如果移动到末尾也没有找到匹配情况,则返回 `-1`

### 思路 4:代码

```Python
class Solution:
Expand Down Expand Up @@ -182,7 +260,27 @@ class Solution:
return boyerMoore(haystack, needle)
```

### 思路 5:Horspool 算法代码
### 思路 4:复杂度分析

- **时间复杂度**:$O(n + \sigma)$,其中文本串 $T$ 的长度为 $n$,字符集的大小是 $\sigma$。
- **空间复杂度**:$O(m)$。其中模式串 $p$ 的长度为 $m$。

### 思路 5:Horspool 算法

**Horspool 算法思想**:对于给定文本串 `T` 与模式串 `p`,先对模式串 `p` 进行预处理。然后在匹配的过程中,当发现文本串 `T` 的某个字符与模式串 `p` 不匹配的时候,根据启发策略,能够尽可能的跳过一些无法匹配的情况,将模式串多向后滑动几位。

Horspool 算法具体步骤如下:

1. 计算出文本串 `T` 的长度为 `n`,模式串 `p` 的长度为 `m`
2. 先对模式串 `p` 进行预处理,生成后移位数表 `bc_table`
3. 将模式串 `p` 的头部与文本串 `T` 对齐,将 `i` 指向文本串开始位置,即 `i = 0``j` 指向模式串末尾位置,即 `j = m - 1`,然后从模式串末尾位置开始比较。
1. 如果文本串对应位置的字符 `T[i + j]` 与模式串对应字符 `p[j]` 相同,则继续比较前一位字符。
1. 如果模式串全部匹配完毕,则返回模式串 `p` 在文本串中的开始位置 `i`
2. 如果文本串对应位置的字符 `T[i + j]` 与模式串对应字符 `p[j]` 不同,则:
1. 根据后移位数表 `bc_table` 和模式串末尾位置对应的文本串上的字符 `T[i + m - 1]` ,计算出可移动距离 `bc_table[T[i + m - 1]]`,然后将模式串进行后移。
4. 如果移动到末尾也没有找到匹配情况,则返回 `-1`

### 思路 5:代码

```Python
class Solution:
Expand Down Expand Up @@ -215,7 +313,27 @@ class Solution:
return horspool(haystack, needle)
```

### 思路 6:Sunday 算法代码
### 思路 5:复杂度分析

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

### 思路 6:Sunday 算法

**Sunday 算法思想**:对于给定文本串 `T` 与模式串 `p`,先对模式串 `p` 进行预处理。然后在匹配的过程中,当发现文本串 `T` 的某个字符与模式串 `p` 不匹配的时候,根据启发策略,能够尽可能的跳过一些无法匹配的情况,将模式串多向后滑动几位。

Sunday 算法具体步骤如下:

1. 计算出文本串 `T` 的长度为 `n`,模式串 `p` 的长度为 `m`
2. 先对模式串 `p` 进行预处理,生成后移位数表 `bc_table`
3. 将模式串 `p` 的头部与文本串 `T` 对齐,将 `i` 指向文本串开始位置,即 `i = 0``j` 指向模式串末尾位置,即 `j = m - 1`,然后从模式串末尾位置开始比较。
1. 如果文本串对应位置的字符 `T[i + j]` 与模式串对应字符 `p[j]` 相同,则继续比较前一位字符。
1. 如果模式串全部匹配完毕,则返回模式串 `p` 在文本串中的开始位置 `i`
2. 如果文本串对应位置的字符 `T[i + j]` 与模式串对应字符 `p[j]` 不同,则:
1. 根据后移位数表 `bc_table` 和模式串末尾位置对应的文本串上的字符 `T[i + m - 1]` ,计算出可移动距离 `bc_table[T[i + m - 1]]`,然后将模式串进行后移。
4. 如果移动到末尾也没有找到匹配情况,则返回 `-1`

### 思路 6:代码

```Python
class Solution:
Expand Down Expand Up @@ -248,4 +366,9 @@ class Solution:
return bc_table

return sunday(haystack, needle)
```
```

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

- **时间复杂度**:$O(n)$。其中文本串 $T$ 的长度为 $n$。
- **空间复杂度**:$O(m)$。其中模式串 $p$ 的长度为 $m$。
8 changes: 6 additions & 2 deletions Solutions/0036. 有效的数独.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@

判断数独有效,需要分别看每一行、每一列、每一个 `3 * 3` 的小方格是否出现了重复数字,如果都没有出现重复数字就是一个有效的数独,如果出现了重复数字则不是有效的数独。

-`3``9 * 9` 的数组分别来表示该数字是否在所在的行,所在的列,所在的方格出现过。其中方格角标的计算用 `box[(i/3)*3+(j/3)][n]` 来表示。
-`3``9 * 9` 的数组分别来表示该数字是否在所在的行,所在的列,所在的方格出现过。其中方格角标的计算用 `box[(i / 3) * 3 + (j / 3)][n]` 来表示。
- 双重循环遍历数独矩阵。如果对应位置上的数字如果已经在在所在的行 / 列 / 方格出现过,则返回 `False`
- 遍历完没有重复出现,则返回 `Ture`

### 思路 1:哈希表代码
### 思路 1:代码

```Python
class Solution:
Expand All @@ -76,3 +76,7 @@ class Solution:
return True
```

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

- **时间复杂度**:$O(1)$。数独总共 81 个单元格,对每个单元格遍历一次,可以看做是常数级的时间复杂度。
- **空间复杂度**:$O(1)$。使用 81 个单位空间,可以看做是常数级的空间复杂度。
1 change: 0 additions & 1 deletion Solutions/0125. 验证回文串.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,3 @@ class Solution:

- **时间复杂度**:$O(len(s))$。
- **空间复杂度**:$O(len(s))$。

18 changes: 18 additions & 0 deletions Solutions/0217. 存在重复元素.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
```Python
输入:nums = [1,2,3,1]
输出:True

输入:nums = [1,2,3,4]
输出:False
```

## 解题思路
Expand All @@ -43,6 +46,11 @@ class Solution:
return False
```

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

- **时间复杂度**:$O(n)$。
- **空间复杂度**:$O(n)$。

### 思路 2:集合

- 使用一个 `set` 集合存储数组中所有元素。
Expand All @@ -57,6 +65,11 @@ class Solution:
return len(set(nums)) != len(nums)
```

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

- **时间复杂度**:$O(n)$。
- **空间复杂度**:$O(n)$。

### 思路 3:排序

- 对数组进行排序。
Expand All @@ -75,3 +88,8 @@ class Solution:
return True
return False
```

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

- **时间复杂度**:$O(n \times \log n)$。
- **空间复杂度**:$O(1)$。
16 changes: 10 additions & 6 deletions Solutions/0219. 存在重复元素 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,22 @@
- 判断哈希表长度是否超过了 `k`,如果超过了 `k`,则删除哈希表中最旧的元素 `nums[i - k]`
- 如果遍历完仍旧找不到,则返回 `False`

### 思路 1:哈希表代码
### 思路 1:代码

```Python
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
nums_set = set()
nums_dict = dict()
for i in range(len(nums)):
if nums[i] in nums_set:
if nums[i] in nums_dict:
return True
nums_set.add(nums[i])
if len(nums_set) > k:
nums_set.remove(nums[i - k])
nums_dict[nums[i]] = 1
if len(nums_dict) > k:
del nums_dict[nums[i - k]]
return False
```

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

- **时间复杂度**:$O(n)$。
- **空间复杂度**:$O(n)$。
33 changes: 29 additions & 4 deletions Solutions/0350. 两个数组的交集 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,36 @@

## 题目大意

给定两个数组,编写一个函数来计算它们的交集。输出结果中,需要考虑元素出现的次数。
**描述**:给定两个数组 `nums1``nums2`

**要求**:返回两个数组的交集。可以不考虑输出结果的顺序。

**说明**

- 输出结果中,每个元素出现的次数,应该与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。
- $1 \le nums1.length, nums2.length \le 1000$。
- $0 \le nums1[i], nums2[i] \le 1000$。

**示例**

```Python
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]


输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
```

## 解题思路

先遍历第一个数组,利用字典来存放第一个数组的元素出现次数。
### 思路 1:哈希表

然后遍历第二个数组,如果字典中存在该元素,则将该元素加入到答案数组中,并减少字典中该元素出现的次数。
1. 先遍历第一个数组,利用字典来存放第一个数组的元素出现次数。
2. 然后遍历第二个数组,如果字典中存在该元素,则将该元素加入到答案数组中,并减少字典中该元素出现的次数。
3. 遍历完之后,返回答案数组。

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

```Python
class Solution:
Expand All @@ -32,3 +53,7 @@ class Solution:
return nums
```

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

- **时间复杂度**:$O(n)$。
- **空间复杂度**:$O(n)$。
37 changes: 32 additions & 5 deletions Solutions/0557. 反转字符串中的单词 III.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,48 @@

## 题目大意

给定一个字符串 s,将字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
**描述**:给定一个字符串 `s`

**要求**:将字符串中每个单词的字符顺序进行反装,同时仍保留空格和单词的初始顺序。

**说明**

- $1 \le s.length \le 5 * 10^4$。
- `s` 包含可打印的 ASCII 字符。
- `s` 不包含任何开头或结尾空格。
- `s` 里至少有一个词。
- `s` 中的所有单词都用一个空格隔开。

**示例**

```Python
输入:s = "Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"


输入: s = "God Ding"
输出:"doG gniD"
```

## 解题思路

### 思路 1:使用额外空间

因为 Python 的字符串是不可变的,所以在原字符串空间上进行切换顺序操作肯定是不可行的了。但我们可以利用切片方法。

- 将字符串按空格进行分割,分割成一个个的单词。
- 再将每个单词进行反转。
- 然后再将每个单词连接起来
1. 将字符串按空格进行分割,分割成一个个的单词。
2. 再将每个单词进行反转。
3. 最后将每个单词连接起来

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

```Python
class Solution:
def reverseWords(self, s: str) -> str:
return " ".join(word[::-1] for word in s.split(" "))
```

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

- **时间复杂度**:$O(n)$。
- **空间复杂度**:$O(n)$。
Loading

0 comments on commit b4ffe82

Please sign in to comment.