题目
这个问题是经典的字符串匹配算法,鼎鼎大名的KMP,之前一直没弄懂,今天又看了一遍,比之前理解更深一点了,把代码也写了一遍。
思路
算法的逻辑是:
- 求解next数组,next数组是模式串的相同的前后子串的最大长度。
- 匹配逻辑根据next数组进行回退,减少比较次数。当有字符不匹配时,根据next[j-1]的值不断更新,直到遇到相等的值或者j又返回了0,返回0就相当于从头开始匹配了。
为啥可以根据next数组来回退?因为这里记录的是前i个元素(包含i)得最长公共前后缀,如果遇到不相等的字符后,说明前面是匹配的,所以可以跳过和匹配字符相同的那几个字符,这个就从next中获取,相同的前后缀就知道当前不匹配的字符前面有哪几个相同,这样的话跳过前缀的相同字符,从不同的字符开始,这样减少比较次数。
/**
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
function getNext(needle) {
let j = 0;
let next = [0];
for (let i = 1; i < needle.length; i++) {
while (j > 0 && needle[i] !== needle[j]) {
j = next[j - 1];
}
if (needle[i] === needle[j]) {
j++;
}
next[i] = j;
}
return next;
}
var strStr = function(haystack, needle) {
const next = getNext(needle);
let j = 0;
for (let i = 0; i < haystack.length; i++) {
// 先处理不相同,否则j++后会不一样
while (j > 0 && haystack[i] !== needle[j]) {
j = next[j - 1];
}
if (haystack[i] === needle[j]) {
j++;
}
if (j === needle.length) { // 起始位置为i的位置减去needle的长度
return i - needle.length + 1;
}
}
return -1;
};