虽然是一道简单题,但是这道题涉及到了KMP算法,值得新手利用本题进行学习和掌握KMP算法的本质,在了解本质的基础上背板。
先介绍一下朴素的解法(摘自LeetCode大佬 宫水三叶)
class Solution {
public:
int strStr(string s, string p) {
int n = s.size(), m = p.size();
for(int i = 0; i <= n - m; i++){
int j = i, k = 0;
while(k < m and s[j] == p[k]){
j++;
k++;
}
if(k == m) return i;
}
return -1;
}
};
作者:宫水三叶
链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
基本上大家第一次做基本都是这个思路,但是这个方法在不考虑剪枝的情况下时间复杂度是O(m*n),而通过KMP算法可以将时间复杂度降低到O(m+n)。
首先先来了解一下什么是KMP算法?
KMP 算法是一个快速查找匹配串的算法,它的作用其实就是本题问题:如何快速在「原字符串」中找到「匹配字符串」。通过将「非完全匹配」的过程中提取到的有效信息加以复用,达到减少「重复匹配」的目的。具体介绍也可以参考:(52 封私信 / 80 条消息) 如何更好地理解和掌握 KMP 算法? - 知乎 (zhihu.com)
KMP算法的关键是解决前缀表next数组有何作用和如何实现。
关于作用:利用前缀表,将模式串各个字串的最大相同前后缀数量标示出来,在匹配过程中遇到不匹配的情况,可以利用最大相同前后缀数量,省略很多不必要的重复匹配过程(跳过不可能匹配成功的匹配过程)。
关于如何实现:接下来关于KMP匹配过程的介绍内容都摘自宫水三叶大佬的题解内容
然后我们再看看「KMP 匹配」过程:
首先匹配串会检查之前已经匹配成功的部分中里是否存在相同的「前缀」和「后缀」。如果存在,则跳转到「前缀」的下一个位置继续往下匹配:
跳转到下一匹配位置后,尝试匹配,发现两个指针的字符对不上,并且此时匹配串指针前面不存在相同的「前缀」和「后缀」,这时候只能回到匹配串的起始位置重新开始:
了解了实现流程,那么接下来介绍具体如何构建next数组,可参考代码随想录:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
首先我们要了解的是构建next数组只和匹配字符串有关,所以我们也只需要编写和匹配字符串有关的代码。
代码如下:
vector<int> next(m);//构造了一个int类型的数组,长度是m,m指匹配字符串的长度
//初始化next数组
next[0]=0;
for(int i=1,j=0;i<m;i++){
//j在这标识已经该子串的最大前后缀的前缀的末尾位置
while(j&&p[i]!=p[j]) j=next[j-1];
//代码含义是:一旦遇到不相等的情况,即无法构成更长的前后缀时,
//则回退到字串j-1的情况,再次判断是否相等,相等则意味着可以
//构成更长的前后缀,不相等重复操作,直到相等或j=0,退出循环。
if(p[i]==p[j]){
next[i]=j+1;
//相等意味着可以构成更长的前后缀,最大长度是下标j+1
j++;
//和i一起递增指向下一组比较对象
}else{
//如果j=0并且p[i]!=p[j]则最长前后缀的长度为0
next[i]=0;
}
}
然后是完整的代码
class Solution {
public:
int strStr(string s, string p) {
int n=s.size(),m=p.size();
if(m==0) return 0;
else if(m>n) return -1;
vector<int> next(m);
//初始化next数组
next[0]=0;
for(int i=1,j=0;i<m;i++){
while(j&&p[i]!=p[j]) j=next[j-1];
if(p[i]==p[j]){
next[i]=j+1;
j++;
}else{
next[i]=0;
}
}
//匹配串p和目标串s匹配的过程
for(int i=0,j=0;i<n;i++){
while(j&&s[i]!=p[j]){
j=next[j-1];
//j在这标识已经匹配的字符的末尾位置
}
if(s[i]==p[j]) {
j++;
}
if(j==m) return i-m+1;
//当匹配的数量等于匹配串p的长度的时候,即完全匹配,返回在目标串中的完全匹配串的下标
}
return -1;
}
};