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 73ce833

Browse files
committed
修复和优化语句表述
1 parent 423bcbc commit 73ce833

17 files changed

+1160
-1063
lines changed

‎docs/04_string/04_01_string_basic.md

Lines changed: 214 additions & 95 deletions
Large diffs are not rendered by default.

‎docs/04_string/04_03_string_brute_force.md renamed to ‎docs/04_string/04_02_string_brute_force.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
## 1. Brute Force 算法介绍
22

3-
> **Brute Force 算法**:简称为 BF 算法。中文意思是暴力匹配算法,也可以叫做朴素匹配算法
4-
>
5-
> - **BF 算法思想**:对于给定文本串 $T$ 与模式串 $p,ドル从文本串的第一个字符开始与模式串 $p$ 的第一个字符进行比较,如果相等,则继续逐个比较后续字符,否则从文本串 $T$ 的第二个字符起重新和模式串 $p$ 进行比较。依次类推,直到模式串 $p$ 中每个字符依次与文本串 $T$ 的一个连续子串相等,则模式匹配成功。否则模式匹配失败
3+
> **Brute Force 算法**:简称为 BF 算法,也可以叫做「朴素匹配算法」
4+
>
5+
> - **Brute Force 算法核心思想**:将模式串 $p$ 依次与文本串 $T$ 的每个起点对齐,从左到右逐字符比对;相等则继续,不等则把对齐起点右移一位,直到匹配成功或遍历完文本
66
77
![朴素匹配算法](https://qcdn.itcharge.cn/images/20240511154456.png)
88

99
## 2. Brute Force 算法步骤
1010

11-
1. 对于给定的文本串 $T$ 与模式串 $p,ドル求出文本串 $T$ 的长度为 $n,ドル模式串 $p$ 的长度为 $m$。
12-
2. 同时遍历文本串 $T$ 和模式串 $p,ドル先将 $T[0]$ 与 $p[0]$ 进行比较。
13-
1. 如果相等,则继续比较 $T[1]$ 和 $p[1]$。以此类推,一直到模式串 $p$ 的末尾 $p[m - 1]$ 为止。
14-
2. 如果不相等,则将文本串 $T$ 移动到上次匹配开始位置的下一个字符位置,模式串 $p$ 则回退到开始位置,再依次进行比较。
15-
3. 当遍历完文本串 $T$ 或者模式串 $p$ 的时候停止搜索。
11+
1. 设文本串 $T$ 长度为 $n,ドル模式串 $p$ 长度为 $m$。
12+
2. 从 $T$ 的每个起点 0ドル..n - m$ 依次与 $p$ 对齐,逐字符比较:如果相等则继续,不相等则起点右移一位、$p$ 归零。
13+
3. 如果某次对齐能把 $p$ 的全部字符匹配完,则返回该起点;否则无解。
1614

1715
## 3. Brute Force 算法代码实现
1816

@@ -37,13 +35,25 @@ def bruteForce(T: str, p: str) -> int:
3735

3836
## 4. Brute Force 算法分析
3937

40-
BF 算法非常简单,容易理解,但其效率很低。主要是因为在匹配过程中可能会出现回溯:当遇到一对字符不同时,模式串 $p$ 直接回到开始位置,文本串也回到匹配开始位置的下一个位置,再重新开始比较
38+
BF 简单直观,但因不匹配时会完全回退、重新对齐,存在大量重复比较,效率较低
4139

42-
在回溯之后,文本串和模式串中一些部分的比较是没有必要的。由于这种操作策略,导致 BF 算法的效率很低。最坏情况是每一趟比较都在模式串的最后遇到了字符不匹配的情况,每轮比较需要进行 $m$ 次字符对比,总共需要进行 $n - m + 1$ 轮比较,总的比较次数为 $m \times (n - m + 1) $。所以 BF 算法的最坏时间复杂度为 $O(m \times n)$。
40+
| 指标 | 复杂度 | 说明 |
41+
| ------------ | -------------- | ------------------------------------ |
42+
| 最好时间复杂度 | $O(m)$ | 首个起点即匹配成功 |
43+
| 最坏时间复杂度 | $O(n \times m)$ | 每次都需回退,全部比较 |
44+
| 平均时间复杂度 | $O(n \times m)$ | 一般情况下的复杂度 |
45+
| 空间复杂度 | $O(1)$ | 原地匹配,无需额外空间 |
4346

44-
在最理想的情况下(第一次匹配直接匹配成功),BF 算法的最佳时间复杂度是 $O(m)$。
47+
- 大量回溯导致重复比较,是 BF 变慢的根源。
48+
- 当文本或模式较长时,更应考虑 KMP、BM、Sunday 等改进算法。
49+
50+
## 5. 总结
51+
52+
Brute Force(BF)算法通过将模式串与文本串每个可能的起点逐字符对齐比较,遇到不匹配时起点右移、模式串重头开始。该算法实现简单,空间复杂度为 $O(1),ドル但时间复杂度较高:最好情况下为 $O(m)$(首位即匹配),平均和最坏情况下为 $O(n\times m),ドル适合小规模或一次性匹配场景。
53+
54+
**优点**:实现简单、无需预处理、适合小规模或一次性匹配。
55+
**缺点**:回溯多、效率低,不适合长文本/长模式或多次匹配场景。
4556

46-
在一般情况下,根据等概率原则,平均搜索次数为 $\frac{(n + m)}{2},ドル所以 Brute Force 算法的平均时间复杂度为 $O(n \times m)$。
4757

4858
## 练习题目
4959

‎docs/04_string/04_02_string_single_pattern_matching.md

Lines changed: 0 additions & 143 deletions
This file was deleted.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
## 1. Rabin Karp 算法介绍
2+
3+
> **Rabin Karp(RK)算法**:由 Michael Oser Rabin 与 Richard Manning Karp 于 1987 年提出,是一种利用哈希快速筛查匹配起点的单模式串匹配算法。
4+
>
5+
> - **Rabin Karp 算法核心思想**:给定文本串 $T$ 与模式串 $p,ドル先计算 $p$ 的哈希值,再对 $T$ 的所有长度为 $m=|p|$ 的子串高效计算哈希。借助「滚动哈希」在 $O(1)$ 时间更新相邻子串的哈希,用哈希相等作为快速筛选,仅在相等时再逐字符比对以排除哈希冲突。
6+
7+
## 2. Rabin Karp 算法步骤
8+
9+
### 2.1 Rabin Karp 算法整体流程
10+
11+
1. 设 $n=|T|$、$m=|p|$。
12+
2. 计算模式串哈希 $H(p)$。
13+
3. 计算文本首个长度为 $m$ 的子串 $T_{[0,m-1]}$ 的哈希 $H(T_{[0,m-1]}),ドル并用滚动哈希依次得到其余 $n - m$ 个相邻子串的哈希。
14+
4. 逐一比较 $H(T_{[i,i+m-1]})$ 与 $H(p)$:
15+
- 如果不相等,跳过;
16+
- 如果相等,逐字符核验:完全相同则返回起点 $i,ドル否则继续。
17+
5. 全部位置检查后仍未匹配,返回 $-1$。
18+
19+
### 2.2 滚动哈希算法
20+
21+
实现 RK 的关键是 **滚动哈希**:使相邻子串哈希的更新从 $O(m)$ 降为 $O(1),ドル显著提升效率。
22+
23+
滚动哈希采用 **Rabin fingerprint** 思想:把子串视作 $d$ 进制多项式,基于上一个子串的哈希在 $O(1)$ 时间得到下一个子串的哈希。
24+
25+
下面我们用一个例子来解释一下这种算法思想。
26+
27+
设字符集大小为 $d,ドル用 $d$ 进制多项式哈希表示子串。
28+
29+
举个例子,假如字符串只包含 $a \sim z$ 这 26ドル$ 个小写字母,那么我们就可以用 26ドル$ 进制数来表示一个字符串,$a$ 表示为 0ドル,ドル$b$ 表示为 1ドル,ドル以此类推,$z$ 就用 25ドル$ 表示。
30+
31+
例如 `"cat"` 的哈希可表示为:
32+
33+
$$\begin{aligned} Hash(cat) &= c \times 26^2 + a \times 26^1 + t \times 26^0 \cr &= 2 \times 26^2 + 0 \times 26^1 + 19 \times 26^0 \cr &= 1371 \end{aligned}$$
34+
35+
这种多项式哈希的特点是:相邻子串的哈希可由上一个快速推得。
36+
37+
如果 $cat$ 的相邻子串为 `"ate"`,直接计算其哈希:
38+
39+
$$\begin{aligned} Hash(ate) &= a \times 26^2 + t \times 26^1 + e \times 26^0 \cr &= 0 \times 26^2 + 19 \times 26^1 + 4 \times 26^0 \cr &= 498 \end{aligned}$$
40+
41+
如果利用上一个子串 `"cat"` 的哈希滚动更新:
42+
43+
$$\begin{aligned} Hash(ate) &= (Hash(cat) - c \times 26^2) \times 26 + e \times 26^0 \cr &= (1371 - 2 \times 26^2) \times 26 + 4 \times 26^0 \cr &= 498 \end{aligned}$$
44+
45+
可以看出,这两种方式计算出的哈希值是相同的。但是第二种计算方式不需要再遍历子串,只需要进行一位字符的计算即可得出整个子串的哈希值。这样每次计算子串哈希值的时间复杂度就降到了 $O(1)$。然后我们就可以通过滚动哈希算法快速计算出子串的哈希值了。
46+
47+
将上述规律形式化如下。
48+
49+
给定文本串 $T$ 与模式串 $p,ドル设 $n=|T|$、$m=|p|$、字符集大小为 $d,ドル则:
50+
51+
- 模式串:$H(p)=\sum\limits_{k=0}^{m-1} p_k,円 d^{m-1-k}$;
52+
- 文本首子串:$H(T_{[0,m-1]})=\sum\limits_{k=0}^{m-1} T_k,円 d^{m-1-k}$;
53+
- 滚动关系:$H(T_{[i+1,i+m]})=\big(H(T_{[i,i+m-1]})-T_i,円 d^{m-1}\big),円 d+T_{i+m}$。
54+
55+
为避免溢出与降低冲突,计算时通常对大质数 $q$ 取模(模数宜大且为质数)。
56+
57+
## 3. Rabin–Karp 代码实现
58+
59+
```python
60+
# T: 文本串,p: 模式串,d: 字符集大小(基数),q: 模数(质数)
61+
def rabinKarp(T: str, p: str, d: int, q: int) -> int:
62+
n, m = len(T), len(p)
63+
if m == 0:
64+
return 0
65+
if n < m:
66+
return -1
67+
68+
hash_p, hash_t = 0, 0
69+
70+
# 计算 H(p) 与首个子串的哈希
71+
for i in range(m):
72+
hash_p = (hash_p * d + ord(p[i])) % q
73+
hash_t = (hash_t * d + ord(T[i])) % q
74+
75+
# 使用 pow 的三参形式避免中间溢出
76+
power = pow(d, m - 1, q) # d^(m-1) % q,用于移除最高位字符
77+
78+
for i in range(n - m + 1):
79+
if hash_p == hash_t:
80+
# 避免冲突:逐字符核验
81+
match = True
82+
for j in range(m):
83+
if T[i + j] != p[j]:
84+
match = False
85+
break
86+
if match:
87+
return i
88+
if i < n - m:
89+
# 滚动更新到下一个子串
90+
hash_t = (hash_t - power * ord(T[i])) % q # 去掉最高位字符
91+
hash_t = (hash_t * d + ord(T[i + m])) % q # 加入新字符
92+
93+
return -1
94+
```
95+
96+
## 4. 复杂度与性质
97+
98+
| 指标 | 复杂度 | 说明 |
99+
| ------------ | -------------- | ------------------------------------ |
100+
| 最好时间复杂度 | $O(n-m+1)$ | 无哈希冲突时,仅需 $n-m+1$ 次哈希对比,均为 $O(1),ドル无需逐字符校验 |
101+
| 最坏时间复杂度 | $O(m(n-m+1))\approx O(nm)$ | 每次哈希均冲突,需 $n-m+1$ 次逐字符全量比对,每次 $O(m)$ |
102+
| 平均时间复杂度 | $O(n-m+1)$ | 期望哈希冲突极少,绝大多数位置仅哈希对比,均摊 $O(1)$ |
103+
| 空间复杂度 | $O(1)$ | 仅需常数变量存储哈希值与辅助参数 |
104+
105+
说明:与 BF 相比,RK 通过哈希筛选把大多数不匹配位置在 $O(1)$ 内排除;但哈希冲突会触发逐字符校验,致使最坏复杂度退化。
106+
107+
## 5. 总结
108+
109+
Rabin-Karp(RK)算法通过将模式串和文本子串转化为哈希值,利用「滚动哈希」快速筛查匹配位置,大幅减少无效字符比较。其平均时间复杂度远优于朴素算法,适合大文本和多模式串场景,但哈希冲突时需回退逐字符比对,最坏情况下复杂度与朴素法相同。合理选择哈希参数可有效降低冲突概率,是一种高效且易于扩展的字符串匹配算法。
110+
111+
**优点**:
112+
- 滚动哈希使子串哈希更新为 $O(1),ドル平均性能优于 BF;
113+
- 易于扩展到多模式串场景(统一维护多哈希)。
114+
**缺点**:
115+
- 存在哈希冲突,最坏复杂度可退化至 $O(nm)$;
116+
- 需合理选择基数 $d$ 与大质数模 $q,ドル以降低冲突概率。
117+
118+
## 练习题目
119+
120+
- [0028. 找出字符串中第一个匹配项的下标](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/0001-0099/find-the-index-of-the-first-occurrence-in-a-string.md)
121+
- [0459. 重复的子字符串](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/0400-0499/repeated-substring-pattern.md)
122+
- [0686. 重复叠加字符串匹配](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/0600-0699/repeated-string-match.md)
123+
- [0796. 旋转字符串](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/0700-0799/rotate-string.md)
124+
- [1408. 数组中的字符串匹配](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/1400-1499/string-matching-in-an-array.md)
125+
- [2156. 查找给定哈希值的子串](https://github.com/ITCharge/AlgoNote/tree/main/docs/solutions/2100-2199/find-substring-with-given-hash-value.md)
126+
127+
- [单模式串匹配题目列表](https://github.com/ITCharge/AlgoNote/tree/main/docs/00_preface/00_06_categories_list.md#%E5%8D%95%E6%A8%A1%E5%BC%8F%E4%B8%B2%E5%8C%B9%E9%85%8D%E9%A2%98%E7%9B%AE)
128+
129+
## 参考资料
130+
131+
- 【书籍】数据结构与算法 Python 语言描述 - 裘宗燕 著
132+
- 【文章】[字符串匹配基础(上)- 数据结构与算法之美 - 极客时间](https://time.geekbang.org/column/article/71187)
133+
- 【文章】[字符串匹配算法 - Rabin Karp 算法 - coolcao 的小站](https://coolcao.com/2020/08/20/rabin-karp/)
134+
- 【问答】[string - Python: Rabin-Karp algorithm hashing - Stack Overflow](https://stackoverflow.com/questions/22216948/python-rabin-karp-algorithm-hashing)

0 commit comments

Comments
(0)

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