22##题目地址
33https://leetcode-cn.com/problems/implement-strstr/
44
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+ #思路
627
728本题是KMP 经典题目。
829
930KMP的经典思想就是:** 当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。**
1031
11- 如果对KMP理论基础还不够了解的同学请看[ ] ( )
32+ 如果对KMP理论基础还不够了解的同学请看[ 字符串:KMP是时候上场了(一文读懂系列) ] ( https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug ) 。
1233
13- 定义一个函数getNext来构建next数组, 函数参数为指向next数组的指针,和一个字符串。 然后在函数里完成对next数组的构建。
34+ ** 为了和 [ 字符串:KMP是时候上场了(一文读懂系列) ] ( https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug ) 字符串命名统一,方便大家理解,以下文章统称haystack为文本串, needle为模式串。 **
1435
36+ 都知道使用KMP算法,一定要构造next数组。
1537
16- 这其实就是计算模式串s,前缀表的过程。 主要有如下三步:
38+ #构造next数组
39+
40+ 我们定义一个函数getNext来构建next数组,函数参数为指向next数组的指针,和一个字符串。 代码如下:
41+
42+ ```
43+ void getNext(int* next, const string& s)
44+ ```
45+
46+ ** 构造next数组其实就是计算模式串s,前缀表的过程。** 主要有如下三步:
1747
18481 . 初始化
19492 . 处理前后缀不相同的情况
20503 . 处理前后缀相同的情况
2151
52+ 接下来我们详解详解一下。
53+
22541 . 初始化:
2355
2456定义两个指针i和j,j指向前缀终止位置(严格来说是终止位置减一的位置),i指向后缀终止位置(与j同理)。
2557
2658然后还要对next数组进行初始化赋值,如下:
2759
2860```
29- int j = -1;
30- next[0] = j;
61+ int j = -1;
62+ next[0] = j;
3163```
3264
3365j 为什么要初始化为 -1呢,因为之前说过 前缀表要统一减一的操作,所以j初始化为-1。
@@ -45,7 +77,7 @@ next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是
4577所以遍历模式串s的循环下表i 要从 1开始,代码如下:
4678
4779```
48- for(int i = 1; i < s.size(); i++) {
80+ for(int i = 1; i < s.size(); i++) {
4981```
5082
5183如果 s[ i] 与 s[ j+1] 不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回溯。
@@ -80,29 +112,31 @@ next[i] = j;
80112最后整体构建next数组的函数代码如下:
81113
82114```
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]; // 向前回溯
94121 }
122+ if (s[i] == s[j + 1]) { // 找到相同的前后缀
123+ j++;
124+ }
125+ next[i] = j; // 将j(前缀的长度)赋给next[i]
95126 }
127+ }
96128```
97129
98130
99- 代码构造next数组的逻辑流程如下 :
131+ 代码构造next数组的逻辑流程动画如下 :
100132
101133<img src =' ../../media/video/KMP精讲3.gif ' width =600 > </img ></div >
102134
103135
104136得到了next数组之后,就要用这个来做匹配了。
105137
138+ #使用next数组来做匹配
139+
106140在文本串s里 找是否出现过模式串t。
107141
108142定义两个下表j 指向模式串起始位置,i指向文本串其实位置。
@@ -150,67 +184,62 @@ if (j == (t.size() - 1) ) {
150184那么使用next数组,用模式串匹配文本串的整体代码如下:
151185
152186```
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+ }
165199```
166200
167201此时所有逻辑的代码都已经写出来了,本题整体代码如下:
168202
169-
170-
171-
172-
173203##C++代码
174204
175205```
176206class Solution {
177207public:
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+ }
191221 int strStr(string haystack, string needle) {
192222 if (needle.size() == 0) {
193223 return 0;
194224 }
195225 int next[needle.size()];
196226 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 寻找之前匹配的位置
202231 }
203- if (haystack[i] == needle[j + 1]) {
204- j++;
232+ if (haystack[i] == needle[j + 1]) { // 匹配,j和i同时向后移动
233+ j++; // i的增加在for循环里
205234 }
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);
208237 }
209238 }
210239 return -1;
211240 }
212241};
213242
214243```
215- > 更过算法干货文章持续更新 ,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
244+ > 更多算法干货文章持续更新 ,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
216245