KMP:字符串匹配算法
目的:希望在字符串匹配过程中,遇到不能匹配的字符时 尽可能把模式串(搜索的字符串)多往后移动几位。
思路:用已知推未知
例如:下面两字符串比较到第7个字段时,发现不匹配,常规匹配法把模式串往后移动一位再进行比较,而KMP可以将字符串往后移动3位,对于这种已匹配模式串包含公共子串的场景,是不是这样更高效一点呢。
主串: | abcabcabcdx | |
模式串: | abcabcdx | |
常规匹配 | 主串: | abcabcabcdx |
模式串: | abcabcdx | |
KMP | 主串: | abcabcabcdx |
模式串: | abcabcdx |
其实这都要归功于next数组,比如上面的示例程序,在下标 6 处发现不匹配,通过next(5) 得到下标 2,也就是主串位置5 对应 模式串的位置 2,后面我们从6 和 模式串 3开始比较。
为啥是next(5)呢?因为下标6前面的才是已经比较过的,属于已知,已知串中如果有公共前缀子串才能用next数组跳转。下面是next数组的示例程序,可以参考下。
生成next数组示例程序:
// b表示模式串,m表示模式串的长度
var b = 'abcabcabxabcabcabxy'.split('');
function getNexts(b, m) {
var next = [];
next[0] = -1;
var k = -1;
for (var i = 1; i < m; ++i) {
while (k != -1 && b[k + 1] != b[i]) {
k = next[k];
}
if (b[k + 1] == b[i]) {
++k;
}
next[i] = k;
}
return next;
}
getNexts(b,b.length);
next数组生成过程像是动态规划的思想,每一个字符都基于前一个字符已经匹配的位置再继续向下匹配的,不需要从头开始匹配。例如下图生成的next数组。
- 前3个字符一眼看过去没有公共子串,我们先跳过。
- 从下标3 的第4个字符开始,我们发现 a 与 下标为 0 的 a一样,所以next[3] = 0。(其实这个过程是先看了一下他前面的next[2] 发现是-1,没法继续匹配,所以跳转到第一个字符重新开始)
- 当处理到下标为4的字符 b 时,先看一下前面的 next[3] = 0,噢原来下标3与下标 0字符一样,所以可以从下标 1 处开始比较,发现也一样 next[4] = 1,后面字符以此类推。这种动态规划的方式是不是前面公共子串越长,节省的比较次数越多呢!
next数组生成 和 匹配过程中的跳转示例图:
文章如何有错误的地方,欢迎大家留言指正。
朋友们如果有好的算法学习方法也可以留言讨论啊!一起学习,一起进步!