leetcode28. 实现 strStr()
字符串匹配问题,想到采用暴力循环对比方法和KMP方法,暂时上传朴素方法,KMP待学习完之后发布= =
KMP算法已更新在朴素暴力算法的下方,是个人的一些理解,如有错误的地方敬请斧正
代码中最难以理解的部分是k=next[k] 这一句,如果阅读后有没明白的地方可以去https://www.cnblogs.com/dusf/p/kmp.html这位作者的博客,讲的非常详细,作者本人也受到这篇博文的启发很多
class Solution {
public int strStr(String haystack, String needle) {
int len_n = haystack.length(), len_p = needle.length();
if(len_p == 0) {
return 0;
}//题意,needle == 0时直接返回0
char[] s = haystack.toCharArray()
char[] p = needle.toCharArray();
//上面两行的作用是toachararray方法,简单来讲是将字符串数组转化为字符函数
for (int i = 0; i <= len_n - len_p; i++) {
int a = i, b = 0;
while (b < len_p && s[a] == p[b]) {
a++;
b++;
}//匹配(双指针思想)
if (b == len_p) {
return i;
}//匹配成功
}
return -1;
}
}
KMP解法↓
先直接上代码和注释,后面有思路
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0) {
return 0;
}
int n = haystack.length(),m = needle.length();//读取长度
haystack = " " + haystack;
needle = " "+ needle;//这里参考leetcode 宫水三叶 的用法,在原串和匹配串前面都加空格,使下标从1开始,更易理解一点
char s[] = haystack.toCharArray();
char p[] = needle.toCharArray();
int next[] = new int [m+1];//构建next数组
for(int i = 2,j = 0;i <= m;i++) {
while(j > 0 && p[i] != p[j+1]) {
j = next[j];
}//每次循环结束后更新i的值(i+=1),使next数组的第i位其始终与符合条件的haystack前缀的最后一位相等(回溯)
if(p[i] == p[j+1]) {
j++;//成功的话,j++(指针后移判断下一个字符);再让i++与其作比较
}
next[i] = j;//匹配不成功时,将j(前缀的长度)赋给next[i];
}
for(int i = 1,j = 0;i <= n;i++) {
while(j > 0 && s[i] != p[j+1]) {
j = next[j];//匹配不成功,寻找之前匹配的位置
}
if(s[i] == p[j + 1]) {
j++;//匹配成功情况,两个指针同时后移
}
if(m == j) {
return i-j;//全部匹配成功,直接返回下标
}
}
return -1;
}
}
代码写了这么多,那么下面讲解思路,KMP的重点在于当某一个字符与被匹配串中不匹配时,需要知道j指针要移动到什么地方
//下图来自于https://www.cnblogs.com/dusf/p/kmp.html这位博主,方便各位理解
这是指针变化的点,即第一次找到不同节点
在C和D不匹配的时候,由于发现前面有一个A,我们显然应该把j挪动到第1位
下图也是一样的情况:
我们可以把指针j挪到第二位,显然是因为AB字母相同
因此,我们可以说,当匹配失败的时候,j要移动的下一个地方最前面的k个字符和j之前的最后k个字符是相等的,数学公式表示为:
needle[0 ~ k-1] == needle[j-k ~ j-1]
觉得不好理解的话,上图:
也就是说能推出以下结论:
因为当haystack[i] != needle[j]时
有haystack[i-j ~ i-1] == needle[0 ~ j-1]
而又由needle[0 ~ k-1] ==needle[j-k ~ j-1]
必然能推出haystack[i-k ~ i-1] == needle[0 ~ k-1]这一结论;从而理解KMP算法。
但是在这样的公式中,如何得到k值是一个关键
由于在被查找关键字的每一个位置都可能发生不匹配,也就是说我们要计算每一个位置j所对应的k,所以用一个数组next来保存;
不好理解的关键代码:**next[j] = k;**所表示的是当 haystack[i] !=needle[j]时(即第一个不重复的字符出现开始),j指针会到达的恒下一个位置,因为k值实际上是j位前的字串的最大重复子串的长度
我们仔细观察下面两张图
对比
我们可以发现一个规律,当子串needle[k] == needle[j]时,可以推出公式
next[j+1] == next[j]+1
那么当needle[k] != needle[j]的时候呢,会有下图这种情况
从代码上来看的话,本图的含义即是k = next[k];原因如下图所示:
按上面的图例来讲,我们已经不可能找到[ A,B,A,B ]这个最长的后缀串了,但我们还是可能找到[ A,B ]、[ B ]这样的前缀串的。所以这个过程像不像在定位[ A,B,A,C ]这个串;当C和主串不一样了(也就是k位置不同时),就把指针移动到next[k]的位置上