力扣第28题可以用来练手kmp算法。
next数组的实现
先看代码
public int[] kmp(String s) {
int j = 0;
int k = -1;
int[] next = new int[s.length()];
// 这里是为了后续匹配的时候作为哨兵使用,并且也方便让next[1] = 0
next[0] = -1;
while (j < s.length() - 1) {
if (k == -1 || s.charAt(j) == s.charAt(k)) {
// 单个字符前后缀是同一个字母所以第一个字符的next值为0
// 此后每成功匹配一个字符,相应的next值就会+1
next[++j] = ++k;
} else {
// 难点在于匹配失败时为什么k会回到next[k]这里
k = next[k];
}
}
return next;
}
当时主要被k=next[k]这里绊了很久。next[k]的意思不仅可以理解为最长相同前后缀的长度,还可以理解为前缀的最后一个字符后面一个字符的索引。k=next[k]可以理解为k由指向最长相同前后缀 中前缀 的末尾改为指向 最长相同前后缀 中的 最长相同前后缀的末尾,在最长前后缀里 寻找最长前后缀进行匹配。假设此时最长相同前后缀是"abab",k原本指向了"abab"后面一个字符去与s[j]匹配,现在指向了"abab"中第二个’a’去与s[j]匹配。目的是省去前面两个字符’a’,‘b’,的匹配,因为此时s[j],前面也有"abab"。有点绕,需要自己画图去理解。
可以看如下分析:
假设当s[k-1]==s[j-1]时,我们假设此时最长相同前后缀为"abab",即s[0]到s[k-1]分别是’a’,‘b’,‘a’,‘b’,
s[j-k]到s[j-1]也是’a’,‘b’,‘a’,‘b’。此时next[j]的值是4,next[k]的值是2。我们可以发现,next[k]表示的是以索引j-1为尾的最长相同前后缀的最长相同前后缀,即"abab"的最长相同前后缀是"ab"。
当s[k] == s[j]时,正常匹配成功,next[j+1]=k+1。
当s[k] != s[j]时,k需要向前回溯,回溯的位置是next[k],如上例子就是k和j匹配完"abab"后,k指向下一个字符比如’e’,j指向下一个字符’a’,匹配失败k要回溯,k回溯到next[k]即回到"abab"的最长前后缀"ab"的后面,用’a’匹配 j 指向的’a’,匹配成功则此时最长前后缀长度为3,可以继续向后匹配,匹配失败则k一直回溯,直到k = next[0] = -1,就回到了最开始的情况。
而若是k回溯到k-1即指向’b’,此时即使假设j指向的下一个字符也是’b’,匹配成功,因为k的前一个字符是’a’,j的前一个字符是’b’,二者最长前后缀从这里开始,长度就变成了1,匹配失败则继续向前回溯直到k=next[k]才会变成上面的情况。可以看到若是匹配成功最长前后缀便会从3变成1,是虚假的最长前后缀,若是失败也要比上面的情况多走好几步才行。
如果一个一个向前回溯的话,就会和暴力匹配字符串一样低效率了,那么为了减少不必要的匹配也需要一个最长前后缀,可以理解为在kmp算法里面使用kmp算法,这也是上面说到最长前后缀的最长前后缀的原因。
因为要避免暴力匹配字符串中的不必要匹配,所以使用kmp算法,利用最长前后缀。而kmp算法中next数组的实现本质上也是字符串的匹配,同样可以利用到最长前后缀减少不必要的匹配。
这里举的例子比较简单,主要是为了方便理解,大家可以自己找一个字符串,它的最长前后缀也拥有最长前后缀,自己按照算法走一遍会清晰很多。比如"ababcababa"。
使用next数组进行字符串匹配
public int strStr(String haystack, String needle) {
if (needle.length() == 0)
return 0;
int[] next = kmp(needle);
for (int i = 0,j = 0; i < haystack.length() && j < needle.length();) {
if (j == -1 || haystack.charAt(i) == needle.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
if (j >= needle.length()) {
return i - j;
}
}
return -1;
}
i 是文本串的索引,j 是模式串的索引。
没啥说头,相同两个字符串一起走,不相同 j 往后退,直到退到next[0]也就是 -1,然后继续匹配,i 到头 j 没到头就是失败,j 能到头说明成功。
以上均为个人理解,难免会有不足和错误,希望各位不吝指正。