一.相关链接
题目链接:28. 实现 strStr()
二.心得体会
1)KMP算法的思想
需要在一个字符串里找另一个字符串就需要用到KMP算法。KMP算法的重点是求出next数组,然后每次遇到不匹配的情况就根据数组来回退并重新匹配。
KMP算法的思想是最长相等前后缀,也就是省去再匹配前面子串的时间。
2)求next数组的流程
KMP算法求next数组的基本流程是:初始化,处理前后缀不匹配情况,处理前后缀匹配情况。
- 初始化:i设置为后缀末尾,j设置为前缀末尾(同时也代表了j前方的子串已经匹配成功),next[0]=0(第一个字母没有前后缀)。
- 处理前后缀不匹配的情况:如果不匹配,我们就需要一直向前回退,根据next[j-1]来一直回退到字符串头或者匹配为止(只有匹配了才说明存在某个前后缀长度相等且相同),本质就是不断缩小前后缀的长度以求找到匹配的最长相等前后缀。
- 处理前后缀匹配的情况:如果匹配,我们就可以j++,然后赋值next[i]=j,说明最长相等前后缀延长一位!
3)问题与思考
1. j的大小和所指字符的含义是什么?
答:j的大小指的是上一个子串已经成功匹配的最长前后缀长度。j所指字符是新加入子串的字符,代表当前需要进行匹配的后缀末尾。
2. 为什么是根据next[j-1]进行回退?
答:第一个解释是根据不变量原则,本质上求next数组也是一个小的查找子串的问题,因此和整个KMP所解决的问题是一致的,和KMP的流程是一样的。第二个解释是回退的本质是不断缩小前后缀长度的一个不断妥协的过程,这个过程我们可以逐个逐个向前回退来比较,但next数组记录了能够匹配的前缀位置,那么只需要让i所指字符和这些匹配了的前缀末尾来比较就可以确定next[i]了。
三.代码
1)实现KMP:
class Solution {
public:
void getNextarray(int* next,string needle){
//初始化
int j=0;
next[0]=0;
//创建next数组
for(int i=1;i<needle.size();i++){
//处理前后缀不匹配情况
while(j>0&&needle[i]!=needle[j]){
j = next[j-1];
}
//处理前后缀匹配情况
if(needle[i] == needle[j]){
j++;
}
//赋值
next[i] = j;
}
}
int strStr(string haystack, string needle) {
const int len = needle.size();
int next[needle.size()];
getNextarray(next,needle);
int j=0;
//进行KMP搜索
for(int i=0;i<haystack.size();i++){
//不匹配就根据next数组回退
while(j>0&&haystack[i]!=needle[j]){
j = next[j-1];
}
if(haystack[i]==needle[j]){
j++;
}
if(j==needle.size()){
//匹配完成后i指向neddle末尾,因此需要复位到开头
return (i-needle.size()+1);
}
}
return -1;
}
};
2)库函数:
class Solution {
public:
int strStr(string haystack, string needle) {
auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
if (result != haystack.end())
return (result - haystack.begin());
else return -1;
}
};