前言
最近在刷算法题的时候遇到了一题要我们实现indexof这个函数,我在做的时候用的是很暴力的算法,应该是叫BF算法,但是我想到之前上数据结构课的时候老师说过一种算法是不用逐个逐个匹配的,可以根据一定的规律跳着匹配,后来我在评论中看到这种算法叫KMP算法。
KMP算法
kmp算法就是在匹配子串中找到相同的前缀和后缀,例如
a b c d a b d
在字符d的前面的子串中,前面有一个ab,后面也有一个ab,那么当主串与子串匹配到d的时候,如果不匹配,BF算法是从主串的下一个字符重新开始匹配子串,但是KMP算法是这样的,因为d前面的字符串都是匹配的,而子串中又有相同的前缀和后缀,那么就可以说明,与后缀匹配的主串与前缀也是匹配的,所以当下一次匹配的时候,我们可以从前缀的后一个字符开始匹配如下图
所以在一开始的时候,我们要对子串构建一个临时数组,这个数组存放的是从这一位字符开始,最长的相同的前缀后缀的长度,就拿上面的abcdabd来举例子
a | b | c | d | a | b | d |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 2 | 0 |
然后,当每次匹配失败的时候,就查找匹配失败字符的前一位字符对应的数组上的数字,也就是找到当前匹配失败的字符前面的字符串的最长匹配的前缀和后缀,下一次主串与子串匹配的时候,根据上面找到的这个数字,从子串中对应的索引来进行匹配,例如
主串:A B C D A B X A B C D A B D
子串:A B C D A B D
当匹配到x的时候,子串对应的D不匹配,这时候查找D前面字符的数字,是2,也就是前缀ab和后缀ab,他们的长度就是2,所以,下一次查找的时候,从数组索引为2的字符开始匹配,也就是c,下一次匹配
主串 A B C D A B X A B C D A B D
子串 A B C D A B D
X与C不匹配,查找C前一位对应的数字,是0,说明没有匹配的前缀后缀
那么下一次就从子串的头开始匹配
主串A B C D A B X A B C D A B D
子串 A B C D A B D
匹配成功!
我讲的可能有一些难懂,具体可以上B站:av3246487,这个人讲的比较具体,每一个步骤都详细讲了
达成成就:B站学习
附上我学习了KMP之后解题的代码
/**
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
var strStr = function(haystack, needle) {
if(needle === "")return 0;
var index = 0;
var tempArray = [0];
//求临时数组
for(var index2 = 1; index2 < needle.length;){
if(needle[index] === needle[index2]){
tempArray[index2] = index + 1;
index2++;
index++;
}else{
if(index != 0){
index = tempArray[index - 1];
}else{
tempArray[index2] = 0;
index2++;
}
}
}
//利用KMP算法解
var i = 0;
var j = 0;
while(i < haystack.length && j < needle.length){
if(haystack[i] === needle[j]){
i++;
j++;
}else{
if(j != 0){
j = tempArray[j - 1];
}else{
i++;
}
}
}
if(j === needle.length){
return i-j;
}
return -1;
};