20
20
``` Python
21
21
输入:haystack = " hello" , needle = " ll"
22
22
输出:2
23
+ 解释:" sad" 在下标 0 和 6 处匹配。第一个匹配项的下标是 0 ,所以返回 0 。
24
+
25
+ 输入:haystack = " leetcode" , needle = " leeto"
26
+ 输出:- 1
27
+ 解释:" leeto" 没有在 " leetcode" 中出现,所以返回 - 1 。
23
28
```
24
29
25
30
## 解题思路
26
31
27
32
字符串匹配的经典题目。常见的字符串匹配算法有:BF(Brute Force)算法、RK(Robin-Karp)算法、KMP(Knuth Morris Pratt)算法、BM(Boyer Moore)算法、Horspool 算法、Sunday 算法等。
28
33
29
- ### 思路 1:BF(Brute Force)算法代码
34
+ ### 思路 1:BF(Brute Force)算法
35
+
36
+ ** BF 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,从文本串的第一个字符开始与模式串 ` p ` 的第一个字符进行比较,如果相等,则继续逐个比较后续字符,否则从文本串 ` T ` 的第二个字符起重新和模式串 ` p ` 进行比较。依次类推,直到模式串 ` p ` 中每个字符依次与文本串 ` T ` 的一个连续子串相等,则模式匹配成功。否则模式匹配失败。
37
+
38
+ BF 算法具体步骤如下:
39
+
40
+ 1 . 对于给定的文本串 ` T ` 与模式串 ` p ` ,求出文本串 ` T ` 的长度为 ` n ` ,模式串 ` p ` 的长度为 ` m ` 。
41
+ 2 . 同时遍历文本串 ` T ` 和模式串 ` p ` ,先将 ` T[0] ` 与 ` p[0] ` 进行比较。
42
+ 1 . 如果相等,则继续比较 ` T[1] ` 和 ` p[1] ` 。以此类推,一直到模式串 ` p ` 的末尾 ` p[m - 1] ` 为止。
43
+ 2 . 如果不相等,则将文本串 ` T ` 移动到上次匹配开始位置的下一个字符位置,模式串 ` p ` 则回退到开始位置,再依次进行比较。
44
+ 3 . 当遍历完文本串 ` T ` 或者模式串 ` p ` 的时候停止搜索。
45
+
46
+ ### 思路 1:代码
30
47
31
48
``` Python
32
49
class Solution :
@@ -50,8 +67,28 @@ class Solution:
50
67
return - 1
51
68
```
52
69
70
+ ### 思路 1:复杂度分析
71
+
72
+ - ** 时间复杂度** :平均时间复杂度为 $O(n + m),ドル最坏时间复杂度为 $O(m \times n)$。其中文本串 $T$ 的长度为 $n,ドル模式串 $p$ 的长度为 $m$。
73
+ - ** 空间复杂度** :$O(1)$。
74
+
75
+ ### 思路 2:RK(Robin Karp)算法
76
+
77
+ ** RK 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,通过滚动哈希算快速筛选出与模式串 ` p ` 不匹配的文本位置,然后在其余位置继续检查匹配项。
78
+
79
+ RK 算法具体步骤如下:
80
+
81
+ 1 . 对于给定的文本串 ` T ` 与模式串 ` p ` ,求出文本串 ` T ` 的长度为 ` n ` ,模式串 ` p ` 的长度为 ` m ` 。
82
+ 2 . 通过滚动哈希算法求出模式串 ` p ` 的哈希值 ` hash_p ` 。
83
+ 3 . 再通过滚动哈希算法对文本串 ` T ` 中 ` n - m + 1 ` 个子串分别求哈希值 ` hash_t ` 。
84
+ 4 . 然后逐个与模式串的哈希值比较大小。
85
+ 1 . 如果当前子串的哈希值 ` hash_t ` 与模式串的哈希值 ` hash_p ` 不同,则说明两者不匹配,则继续向后匹配。
86
+ 2 . 如果当前子串的哈希值 ` hash_t ` 与模式串的哈希值 ` hash_p ` 相等,则验证当前子串和模式串的每个字符是否真的相等(避免哈希冲突)。
87
+ 1 . 如果当前子串和模式串的每个字符相等,则说明当前子串和模式串匹配。
88
+ 2 . 如果当前子串和模式串的每个字符不相等,则说明两者不匹配,继续向后匹配。
89
+ 5 . 比较到末尾,如果仍未成功匹配,则说明文本串 ` T ` 中不包含模式串 ` p ` ,方法返回 ` -1 ` 。
53
90
54
- ### 思路 2:RK(Robin-Karp)算法代码
91
+ ### 思路 2:代码
55
92
56
93
``` Python
57
94
class Solution :
@@ -75,7 +112,26 @@ class Solution:
75
112
return rabinKarp(haystack, needle)
76
113
```
77
114
78
- ### 思路 3:KMP(Knuth Morris Pratt)算法代码
115
+ ### 思路 1:复杂度分析
116
+
117
+ - ** 时间复杂度** :$O(n)$。其中文本串 $T$ 的长度为 $n,ドル模式串 $p$ 的长度为 $m$。
118
+ - ** 空间复杂度** :$O(m)$。
119
+
120
+ ### 思路 3:KMP(Knuth Morris Pratt)算法
121
+
122
+ ** KMP 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,当发现文本串 ` T ` 的某个字符与模式串 ` p ` 不匹配的时候,可以利用匹配失败后的信息,尽量减少模式串与文本串的匹配次数,避免文本串位置的回退,以达到快速匹配的目的。
123
+
124
+ KMP 算法具体步骤如下:
125
+
126
+ 1 . 根据 ` next ` 数组的构造步骤生成「前缀表」` next ` 。
127
+ 2 . 使用两个指针 ` i ` 、` j ` ,其中 ` i ` 指向文本串中当前匹配的位置,` j ` 指向模式串中当前匹配的位置。初始时,` i = 0 ` ,` j = 0 ` 。
128
+ 3 . 循环判断模式串前缀是否匹配成功,如果模式串前缀匹配不成功,将模式串进行回退,即 ` j = next[j - 1] ` ,直到 ` j == 0 ` 时或前缀匹配成功时停止回退。
129
+ 4 . 如果当前模式串前缀匹配成功,则令模式串向右移动 ` 1 ` 位,即 ` j += 1 ` 。
130
+ 5 . 如果当前模式串 ** 完全** 匹配成功,则返回模式串 ` p ` 在文本串 ` T ` 中的开始位置,即 ` i - j + 1 ` 。
131
+ 6 . 如果还未完全匹配成功,则令文本串向右移动 ` 1 ` 位,即 ` i += 1 ` ,然后继续匹配。
132
+ 7 . 如果直到文本串遍历完也未完全匹配成功,则说明匹配失败,返回 ` -1 ` 。
133
+
134
+ ### 思路 3:代码
79
135
80
136
``` Python
81
137
class Solution :
@@ -120,7 +176,29 @@ class Solution:
120
176
return kmp(haystack, needle)
121
177
```
122
178
123
- ### 思路 4:BM(Boyer Moore)算法代码
179
+ ### 思路 3:复杂度分析
180
+
181
+ - ** 时间复杂度** :$O(n + m),ドル其中文本串 $T$ 的长度为 $n,ドル模式串 $p$ 的长度为 $m$。
182
+ - ** 空间复杂度** :$O(m)$。
183
+
184
+ ### 思路 4:BM(Boyer Moore)算法
185
+
186
+ ** BM 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,先对模式串 ` p ` 进行预处理。然后在匹配的过程中,当发现文本串 ` T ` 的某个字符与模式串 ` p ` 不匹配的时候,根据启发策略,能够直接尽可能地跳过一些无法匹配的情况,将模式串多向后滑动几位。
187
+
188
+ BM 算法具体步骤如下:
189
+
190
+ 1 . 计算出文本串 ` T ` 的长度为 ` n ` ,模式串 ` p ` 的长度为 ` m ` 。
191
+ 2 . 先对模式串 ` p ` 进行预处理,生成坏字符位置表 ` bc_table ` 和好后缀规则后移位数表 ` gs_talbe ` 。
192
+ 3 . 将模式串 ` p ` 的头部与文本串 ` T ` 对齐,将 ` i ` 指向文本串开始位置,即 ` i = 0 ` 。` j ` 指向模式串末尾位置,即 ` j = m - 1 ` ,然后从模式串末尾位置开始进行逐位比较。
193
+ 1 . 如果文本串对应位置 ` T[i + j] ` 上的字符与 ` p[j] ` 相同,则继续比较前一位字符。
194
+ 1 . 如果模式串全部匹配完毕,则返回模式串 ` p ` 在文本串中的开始位置 ` i ` 。
195
+ 2 . 如果文本串对应位置 ` T[i + j] ` 上的字符与 ` p[j] ` 不相同,则:
196
+ 1 . 根据坏字符位置表计算出在「坏字符规则」下的移动距离 ` bad_move ` 。
197
+ 2 . 根据好后缀规则后移位数表计算出在「好后缀规则」下的移动距离 ` good_mode ` 。
198
+ 3 . 取两种移动距离的最大值,然后对模式串进行移动,即 ` i += max(bad_move, good_move) ` 。
199
+ 4 . 如果移动到末尾也没有找到匹配情况,则返回 ` -1 ` 。
200
+
201
+ ### 思路 4:代码
124
202
125
203
``` Python
126
204
class Solution :
@@ -182,7 +260,27 @@ class Solution:
182
260
return boyerMoore(haystack, needle)
183
261
```
184
262
185
- ### 思路 5:Horspool 算法代码
263
+ ### 思路 4:复杂度分析
264
+
265
+ - ** 时间复杂度** :$O(n + \sigma),ドル其中文本串 $T$ 的长度为 $n,ドル字符集的大小是 $\sigma$。
266
+ - ** 空间复杂度** :$O(m)$。其中模式串 $p$ 的长度为 $m$。
267
+
268
+ ### 思路 5:Horspool 算法
269
+
270
+ ** Horspool 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,先对模式串 ` p ` 进行预处理。然后在匹配的过程中,当发现文本串 ` T ` 的某个字符与模式串 ` p ` 不匹配的时候,根据启发策略,能够尽可能的跳过一些无法匹配的情况,将模式串多向后滑动几位。
271
+
272
+ Horspool 算法具体步骤如下:
273
+
274
+ 1 . 计算出文本串 ` T ` 的长度为 ` n ` ,模式串 ` p ` 的长度为 ` m ` 。
275
+ 2 . 先对模式串 ` p ` 进行预处理,生成后移位数表 ` bc_table ` 。
276
+ 3 . 将模式串 ` p ` 的头部与文本串 ` T ` 对齐,将 ` i ` 指向文本串开始位置,即 ` i = 0 ` 。` j ` 指向模式串末尾位置,即 ` j = m - 1 ` ,然后从模式串末尾位置开始比较。
277
+ 1 . 如果文本串对应位置的字符 ` T[i + j] ` 与模式串对应字符 ` p[j] ` 相同,则继续比较前一位字符。
278
+ 1 . 如果模式串全部匹配完毕,则返回模式串 ` p ` 在文本串中的开始位置 ` i ` 。
279
+ 2 . 如果文本串对应位置的字符 ` T[i + j] ` 与模式串对应字符 ` p[j] ` 不同,则:
280
+ 1 . 根据后移位数表 ` bc_table ` 和模式串末尾位置对应的文本串上的字符 ` T[i + m - 1] ` ,计算出可移动距离 ` bc_table[T[i + m - 1]] ` ,然后将模式串进行后移。
281
+ 4 . 如果移动到末尾也没有找到匹配情况,则返回 ` -1 ` 。
282
+
283
+ ### 思路 5:代码
186
284
187
285
``` Python
188
286
class Solution :
@@ -215,7 +313,27 @@ class Solution:
215
313
return horspool(haystack, needle)
216
314
```
217
315
218
- ### 思路 6:Sunday 算法代码
316
+ ### 思路 5:复杂度分析
317
+
318
+ - ** 时间复杂度** :$O(n)$。其中文本串 $T$ 的长度为 $n$。
319
+ - ** 空间复杂度** :$O(m)$。其中模式串 $p$ 的长度为 $m$。
320
+
321
+ ### 思路 6:Sunday 算法
322
+
323
+ ** Sunday 算法思想** :对于给定文本串 ` T ` 与模式串 ` p ` ,先对模式串 ` p ` 进行预处理。然后在匹配的过程中,当发现文本串 ` T ` 的某个字符与模式串 ` p ` 不匹配的时候,根据启发策略,能够尽可能的跳过一些无法匹配的情况,将模式串多向后滑动几位。
324
+
325
+ Sunday 算法具体步骤如下:
326
+
327
+ 1 . 计算出文本串 ` T ` 的长度为 ` n ` ,模式串 ` p ` 的长度为 ` m ` 。
328
+ 2 . 先对模式串 ` p ` 进行预处理,生成后移位数表 ` bc_table ` 。
329
+ 3 . 将模式串 ` p ` 的头部与文本串 ` T ` 对齐,将 ` i ` 指向文本串开始位置,即 ` i = 0 ` 。` j ` 指向模式串末尾位置,即 ` j = m - 1 ` ,然后从模式串末尾位置开始比较。
330
+ 1 . 如果文本串对应位置的字符 ` T[i + j] ` 与模式串对应字符 ` p[j] ` 相同,则继续比较前一位字符。
331
+ 1 . 如果模式串全部匹配完毕,则返回模式串 ` p ` 在文本串中的开始位置 ` i ` 。
332
+ 2 . 如果文本串对应位置的字符 ` T[i + j] ` 与模式串对应字符 ` p[j] ` 不同,则:
333
+ 1 . 根据后移位数表 ` bc_table ` 和模式串末尾位置对应的文本串上的字符 ` T[i + m - 1] ` ,计算出可移动距离 ` bc_table[T[i + m - 1]] ` ,然后将模式串进行后移。
334
+ 4 . 如果移动到末尾也没有找到匹配情况,则返回 ` -1 ` 。
335
+
336
+ ### 思路 6:代码
219
337
220
338
``` Python
221
339
class Solution :
@@ -248,4 +366,9 @@ class Solution:
248
366
return bc_table
249
367
250
368
return sunday(haystack, needle)
251
- ```
369
+ ```
370
+
371
+ ### 思路 6:复杂度分析
372
+
373
+ - ** 时间复杂度** :$O(n)$。其中文本串 $T$ 的长度为 $n$。
374
+ - ** 空间复杂度** :$O(m)$。其中模式串 $p$ 的长度为 $m$。
0 commit comments