示例 1:算法
输入: haystack = "hello", needle = "ll"
输出: 2
复制代码
示例 2:数组
输入: haystack = "aaaaa", needle = "bba"
输出: -1
复制代码
思路1:暴力匹配bash
假设haystack="abcabcx",needle="abcabx",m为haystack的长度,n为needle的长度, i为字符串haystack的索引,j为字符串needle的索引,依次比较haystack[i]和needle[j],以下图:ui
回溯两个指针,从新比较,直到needle彻底匹配上或haystack字符串循环结束也没有匹配上为止,以下图。spa
暴力匹配方法须要不停的回溯两个指针,时间复杂度为O(m*n)。3d
思路2:kmp算法指针
咱们先观察一下上面第一次不匹配时的状况,为了清晰标注了一下颜色:code
不匹配字符前面的ab(绿色)是已知匹配上的,刚好needle最前面的两个字符也是ab(红色),cdn
这提示咱们能够设法利用红色ab和绿色ab相等来减小匹配的次数。咱们能够把j直接移动到红色ab的下一个字符,i保持不动,继续进行匹配,以下图:blog
继续进行匹配,若是再遇到不匹配字符,重复上述步骤,直到needle彻底匹配上,或者haystack字符串循环结束也没有匹配上为止,以下图:
这样不用回溯i,大大节省了效率。
把上面的步骤用代码实现:
var strStr = function(haystack, needle) {
var m = haystack.length, n = needle.length;
if(!n) return 0;
var next = kmpProcess(needle);
for(var i = 0, j = 0; i < m;) {
if(haystack[i] == needle[j]) { // 字符匹配,i和j都向前一步
i++, j++;
}
if(j == n) return i - j; // needle彻底匹配上,返回匹配位置
if(haystack[i] != needle[j]) { // 字符不匹配
if(j > 0) {
// TODO 如何重置j呢?
} else {
i++;
}
}
}
return -1;
};
复制代码
那么字符不匹配时,怎样让程序知道应该把j重置在什么地方呢?咱们先来观察一下第一次不匹配时的状况:
此时在不匹配字符x前,needle的子串是:abcab,经过肉眼观察,前缀ab和后缀ab相等,字符串"ab"的长度是2,这时咱们要把j重置到needle数组下标为2的地方(数组下标从0开始)。能够按照这个思路把needle中每一个字符不匹配时,重置j的位置对应的记录到一个数组next里,咱们接下来要作的就是求出next数组。
接下来咱们开始计算next数组。最差的状况就是j重置到0,先把数组用0填充。
假设x是遍历needle的索引,y是neddle数组从0开始的索引,也是j将要重置的位置(即存入next数组的值)。由于needle的第一个字符没有先后缀,因此next[0]永远是0,因此x从1开始。计算next的过程以下:
2.needle[x] != neddle[y],因为y=0,因此next[x]=0,而且x向前一步,以下图:
needle[x] == neddle[y],以下图:
needle[x] == neddle[y],以下图:
5.needle[x] != neddle[y],因为y != 0,说明以前有匹配上的字符串,此时须要移动y至next[y-1],即y=0,再去比较needle[x] 与 neddle[y],注意,y !=0 或者needle[x] != neddle[y]时,x不能前进,要保持在原地,以下图:
最后获得next数组为:[0, 0, 0, 1, 2, 0]
把上面两步用代码实现:
var strStr = function(haystack, needle) {
var m = haystack.length, n = needle.length;
if(!n) return 0;
var next = kmpProcess(needle);
for(var i = 0, j = 0; i < m;) {
if(haystack[i] == needle[j]) { // 字符匹配,i和j都向前一步
i++, j++;
}
if(j == n) return i - j; // needle彻底匹配上,返回匹配位置
if(haystack[i] != needle[j]) { // 字符不匹配
if(j > 0) {
j = next[j - 1]; // 重置j
} else {
i++;
}
}
}
return -1;
};
var kmpProcess = function(needle) {
var y = 0;
var x = 1;
var next = new Array(needle.length).fill(0);
while (x < needle.length) {
if (needle[x] == needle[y]) {
y++;
next[x] = y;
x++;
} else if (y > 0) {
y = next[y - 1];
} else {
next[x] = 0;
x++;
}
}
return next;
}
console.log(strStr('abcabcabya', 'abcaby')); // 3
复制代码
kmpProcess的时间复杂度是O(n),不须要回溯haystack的索引i,整个算法的时间复杂度为O(m+n)。