2
2
## 题目地址
3
3
https://leetcode-cn.com/problems/implement-strstr/
4
4
5
- ## 思路
5
+ > 在一个串中查找是否出现过另一个串,这是KMP的看家本领。
6
+
7
+ # 题目:28. 实现 strStr()
8
+
9
+ 实现 strStr() 函数。
10
+
11
+ 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
12
+
13
+ 示例 1:
14
+ 输入: haystack = "hello", needle = "ll"
15
+ 输出: 2
16
+
17
+ 示例 2:
18
+ 输入: haystack = "aaaaa", needle = "bba"
19
+ 输出: -1
20
+
21
+ 说明:
22
+ 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
23
+ 对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
24
+
25
+
26
+ # 思路
6
27
7
28
本题是KMP 经典题目。
8
29
9
30
KMP的经典思想就是:** 当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。**
10
31
11
- 如果对KMP理论基础还不够了解的同学请看[ ] ( )
32
+ 如果对KMP理论基础还不够了解的同学请看[ 字符串:KMP是时候上场了(一文读懂系列) ] ( https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug ) 。
12
33
13
- 定义一个函数getNext来构建next数组, 函数参数为指向next数组的指针,和一个字符串。 然后在函数里完成对next数组的构建。
34
+ ** 为了和 [ 字符串:KMP是时候上场了(一文读懂系列) ] ( https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug ) 字符串命名统一,方便大家理解,以下文章统称haystack为文本串, needle为模式串。 **
14
35
36
+ 都知道使用KMP算法,一定要构造next数组。
15
37
16
- 这其实就是计算模式串s,前缀表的过程。 主要有如下三步:
38
+ # 构造next数组
39
+
40
+ 我们定义一个函数getNext来构建next数组,函数参数为指向next数组的指针,和一个字符串。 代码如下:
41
+
42
+ ```
43
+ void getNext(int* next, const string& s)
44
+ ```
45
+
46
+ ** 构造next数组其实就是计算模式串s,前缀表的过程。** 主要有如下三步:
17
47
18
48
1 . 初始化
19
49
2 . 处理前后缀不相同的情况
20
50
3 . 处理前后缀相同的情况
21
51
52
+ 接下来我们详解详解一下。
53
+
22
54
1 . 初始化:
23
55
24
56
定义两个指针i和j,j指向前缀终止位置(严格来说是终止位置减一的位置),i指向后缀终止位置(与j同理)。
25
57
26
58
然后还要对next数组进行初始化赋值,如下:
27
59
28
60
```
29
- int j = -1;
30
- next[0] = j;
61
+ int j = -1;
62
+ next[0] = j;
31
63
```
32
64
33
65
j 为什么要初始化为 -1呢,因为之前说过 前缀表要统一减一的操作,所以j初始化为-1。
@@ -45,7 +77,7 @@ next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是
45
77
所以遍历模式串s的循环下表i 要从 1开始,代码如下:
46
78
47
79
```
48
- for(int i = 1; i < s.size(); i++) {
80
+ for(int i = 1; i < s.size(); i++) {
49
81
```
50
82
51
83
如果 s[ i] 与 s[ j+1] 不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回溯。
@@ -80,29 +112,31 @@ next[i] = j;
80
112
最后整体构建next数组的函数代码如下:
81
113
82
114
```
83
- void getNext(int* next, const string& s){
84
- int j = -1;
85
- next[0] = j;
86
- for(int i = 1; i < s.size(); i++) { // 注意i从1开始
87
- while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
88
- j = next[j]; // 向前回溯
89
- }
90
- if (s[i] == s[j + 1]) { // 找到相同的前后缀
91
- j++;
92
- }
93
- next[i] = j; // 将j(前缀的长度)赋给next[i]
115
+ void getNext(int* next, const string& s){
116
+ int j = -1;
117
+ next[0] = j;
118
+ for(int i = 1; i < s.size(); i++) { // 注意i从1开始
119
+ while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
120
+ j = next[j]; // 向前回溯
94
121
}
122
+ if (s[i] == s[j + 1]) { // 找到相同的前后缀
123
+ j++;
124
+ }
125
+ next[i] = j; // 将j(前缀的长度)赋给next[i]
95
126
}
127
+ }
96
128
```
97
129
98
130
99
- 代码构造next数组的逻辑流程如下 :
131
+ 代码构造next数组的逻辑流程动画如下 :
100
132
101
133
<img src =' ../../media/video/KMP精讲3.gif ' width =600 > </img ></div >
102
134
103
135
104
136
得到了next数组之后,就要用这个来做匹配了。
105
137
138
+ # 使用next数组来做匹配
139
+
106
140
在文本串s里 找是否出现过模式串t。
107
141
108
142
定义两个下表j 指向模式串起始位置,i指向文本串其实位置。
@@ -150,67 +184,62 @@ if (j == (t.size() - 1) ) {
150
184
那么使用next数组,用模式串匹配文本串的整体代码如下:
151
185
152
186
```
153
- int j = -1; // 因为next数组里记录的起始位置为-1
154
- for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
155
- while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
156
- j = next[j]; // j 寻找之前匹配的位置
157
- }
158
- if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动
159
- j++; // i的增加在for循环里
160
- }
161
- if (j == (t.size() - 1) ) { // 文本串s里出现了模式串t
162
- return (i - t.size() + 1);
163
- }
164
- }
187
+ int j = -1; // 因为next数组里记录的起始位置为-1
188
+ for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
189
+ while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
190
+ j = next[j]; // j 寻找之前匹配的位置
191
+ }
192
+ if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动
193
+ j++; // i的增加在for循环里
194
+ }
195
+ if (j == (t.size() - 1) ) { // 文本串s里出现了模式串t
196
+ return (i - t.size() + 1);
197
+ }
198
+ }
165
199
```
166
200
167
201
此时所有逻辑的代码都已经写出来了,本题整体代码如下:
168
202
169
-
170
-
171
-
172
-
173
203
## C++代码
174
204
175
205
```
176
206
class Solution {
177
207
public:
178
- void getNext(int* next, const string& s) {
179
- next[0] = -1;
180
- int j = -1 ;
181
- for(int i = 1; i < s.size(); i++){
182
- while (j >= 0 && s[i] != s[j + 1]) {
183
- j = next[j];
184
- }
185
- if (s[i] == s[j + 1]) {
186
- j++;
187
- }
188
- next[i] = j;
189
- }
190
- }
208
+ void getNext(int* next, const string& s) {
209
+ int j = -1;
210
+ next[0] = j ;
211
+ for(int i = 1; i < s.size(); i++) { // 注意i从1开始
212
+ while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
213
+ j = next[j]; // 向前回溯
214
+ }
215
+ if (s[i] == s[j + 1]) { // 找到相同的前后缀
216
+ j++;
217
+ }
218
+ next[i] = j; // 将j(前缀的长度)赋给next[i]
219
+ }
220
+ }
191
221
int strStr(string haystack, string needle) {
192
222
if (needle.size() == 0) {
193
223
return 0;
194
224
}
195
225
int next[needle.size()];
196
226
getNext(next, needle);
197
-
198
- int j = -1;
199
- for (int i = 0; i < haystack.size(); i++) {
200
- while(j >= 0 && haystack[i] != needle[j + 1]) {
201
- j = next[j];
227
+ int j = -1; // // 因为next数组里记录的起始位置为-1
228
+ for (int i = 0; i < haystack.size(); i++) { // 注意i就从0开始
229
+ while(j >= 0 && haystack[i] != needle[j + 1]) { // 不匹配
230
+ j = next[j]; // j 寻找之前匹配的位置
202
231
}
203
- if (haystack[i] == needle[j + 1]) {
204
- j++;
232
+ if (haystack[i] == needle[j + 1]) { // 匹配,j和i同时向后移动
233
+ j++; // i的增加在for循环里
205
234
}
206
- if (j == (needle.size() - 1) ) {
207
- return (i - needle.size() + 1);
235
+ if (j == (needle.size() - 1) ) { // 文本串s里出现了模式串t
236
+ return (i - needle.size() + 1);
208
237
}
209
238
}
210
239
return -1;
211
240
}
212
241
};
213
242
214
243
```
215
- > 更过算法干货文章持续更新 ,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
244
+ > 更多算法干货文章持续更新 ,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
216
245
0 commit comments