前言
离闯关挑战的倒计时还有一天的时间,而我还有5篇的文章没有发表出来...不过没关系,即使这个活动结束,我还会坚持刷题打卡,或者发文章的。今天要攻克的是KMP算法,KMP算法的作用其实很好理解,就是在当进行字符串匹配时,让子串尽可能地向前移动更多地距离,从而减少不必要的回溯。KMP算法的核心是next[]数组的代码理解和分析,也是我花了大量精力思考的难点,这里非常绕,还小小地套用了递归的想法,非常难懂也非常巧妙。
题目描述:
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll"
输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba"
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/im…
思路分析:
通读全题会发现,这道题是典型的利用KMP算法解决的问题,KMP算法的核心是避免不必要的回溯。所以,根据题目要求,自然可以通过KMP算法进行字符串的匹配并找到字符串相对应的位置。
- 代码可以分为两个部分,一个是新建的
getNext函数,还有一部分是主函数,主函数包含了对getNext函数的调用和字符串匹配过程的判断。 - 在
getNext中,要注意一点,当j为0和1的时候,对应的next数组中的值都为0,但是为了后边的计算调用更加方便,所以直接设定它为-1,i的值代表了后缀,j为前缀,所以她们两个的值相差为1,当字符串在i和j的位置不匹配,且前缀与后缀不相等时,子串应该向前回溯,即
j=next[j];
回溯的意思为重新寻找位置从哪里开头并进行重新匹配,而next[j]的含义为当子串的第j个字符失配时,在子串中需重新和主串进行比较字符的位置,所以当第j个字符失配时,j需要回溯到next[j].这个用法类似于递归。
而当前缀和后缀相同时,其所对应的next[i]的值与前缀即j的值有关,所以我们可以将前缀的长度赋值给next[i]
- 在主函数中首先先调用getNext函数,获取子串needle的next数组,在for循环中,从i=0开始遍历,当j的值为子串长度减一时,说明已经出现了模式串,即匹配成功,直接返回对应值就可以了。
AC 代码:
KMP
class Solution {
public:
void getNext(int *next,const string &s)
{
int j=-1;
next[0]=j;
for(int i=1;i<s.size();i++)
{
while(j>=0&&s[i]!=s[j+1])
{
j=next[j];
}
if(s[i]==s[j+1])
{
j++;
}
next[i]=j;
}
}
int strStr(string haystack, string needle) {
if(needle.size()==0)
{
return 0;
}
int next[needle.size()];
getNext(next,needle);
int j=-1;
for(int i=0;i<haystack.size();i++)
{
while(j>=0&&haystack[i]!=needle[j+1])
{
j=next[j];
}
if(haystack[i]==needle[j+1])
{
j++;
}
if(j==(needle.size()-1))
{
return (i-needle.size()+1);
}
}
return -1;
}
};
总结:
KMP算法我学了好久。。。这道题也想了好久。。。还是要细心吧,一定要专心,不能遗漏任何细微的知识点。这道题也可以用提示中的indexOf()函数返回匹配成功的位置,这个方法也更加简单。翻看别人的答案,我发现这个依然有人用暴力法解这个题这里就不贴代码了,有兴趣的可以到对应链接的网站上自己做做,然后提交代码,检验是否正确。
谢谢阅读到这里的你,如果觉得我的文章对你有帮助的话,希望你能点个赞,不胜感激!
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情