持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
28.找出字符串中第一个匹配项的下标
分析题目
题目给出两个字符串。要求在第1个字符串中找出第2个字符串第1个匹配项的下标。类似于库函数indexOf()的效果。字符串匹配是用KMP解决的经典题目,这道题就可以用KMP解决。
KMP
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
而KMP的重点就是记录已经匹配的文本内容,也就是next数组。
next数组
next数组就是前缀表,用来回退,记录模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。所以我们需要求出next数组。
首先我们需要了解两个概念。第一个是前缀,前缀是指除了最后一个字符外字符串的全部。第二个是后缀,后缀指除了第一个字符以外字符串的全部。 next值是前缀和后缀最长的公共元素的长度。
接下来需要利用前缀表找到当字符不匹配时候,指针应该移动的位置。如果找到不匹配的位置,我们要看他前一个字符的前缀表数值,将指针移到那个位置。比如前一个字符的前缀表数值是3,我们就把下标移动到下标为3的位置继续匹配。
解决
首先需要构造next数组,通过双指针的方式找到最大相同前后缀的长度,将长度放入next数组内。
接下来就可以使用next数组,将两个字符串进行匹配。如果遇到不匹配的位置,需要找它前缀表的前一个元素的那个next值,这样就可以实现不重复比较。
代码实现如下:
var strStr = function(haystack, needle) {
if (needle.length === 0) return 0;
const getNext = (needle) => {
let next = [];
let j = 0;
next.push(j);
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.push(j);
}
return next;
}
let next = getNext(needle);
let j = 0;
for (let i = 0; i < haystack.length; ++i) {
while (j > 0 && haystack[i] !== needle[j])
j = next[j - 1];
if (haystack[i] === needle[j])
j++;
if (j === needle.length)
return (i - needle.length + 1);
}
return -1;
};
总结
KMP算法很好的解决了字符串匹配的问题,以后还需要多加练习才能记住它!