/* 比如 关键字 key ('abcf') 匹配字符串SS ('abcd abcf') 当比较到d的时候, 发现不对, 那为了节省效率, 可以直跳过abc, 跳跃到d开始再比较, 这里是没问题的 但是当 关键字 key('abcabd') 匹配字符串 SS( 'abcabcabd') 比较到第二个c的时候, 发现d != c ,显然是不能直接跳跃到c开始比较的 应该从第二个ab开始, 要找出这个ab可利用 前缀 = 后缀的方法 跳3位, 就到了ab, 这3位是怎么算出来的, 已经匹配的abcab 5位 - ab的 2位得到 比如 key(abcabd) 切前面5位就是 abcab 前缀是 a ab abc abca 后缀 b ab cab bcab 可以看到 ab是在关键字切割成一部分后, 存在前缀和后缀相等的情况 这种情况在匹配字符串SS 的时候, 第一次遍历SS的时候,如果abca都匹配成功 ,a就匹配了两次, 如果或者abcab都匹配成功, ab就匹配了两次 所以这个关键字可以标记成 [ a, b, c, 1, 2, d ]
反过来
1. 如果一个关键字没有 前缀和后缀相等的情况, 则可以直接跳过已经匹配的, 如
SS: abcdffff
key: abcde
2. 如果有前缀 或者情况, 但匹配到 e的时候才发现不匹配, 而且前一个f在 map表为0, 也是可以直接跳过的
因为既然最后的 abf, 在前缀不存在abf, 所以不需要跳到第二个ab开始去匹配, 直接跳到第二个f就可以了
SS: abcdabf fgggg
key: abcdabf e
3. 再看一个复杂一点的情况,
SS: abc abc accab
key: abc abc ab
map: 000 123 12
根据前面 已匹配 - 上一个map值; 会跳到第3个a 去比较, 为什么不跳到第二个abc开始呢,
*/ function getMap(str) { var prefix = []; var suffix = []; var map = []; for (var i = 0, j = str.length; i < j; i++) { var newStr = str.substring(0, i + 1); if (newStr.length == 1) { map[i] = 0; } else { //每跳一个字符, 就查看这截字符串 有没有前缀后缀匹配的情况 for (var k = 0; k < i; k++) { //取前缀 prefix[k] = newStr.slice(0, k + 1); //取后缀 suffix[k] = newStr.slice(-k - 1); //如果前后缀相等 if (prefix[k] == suffix[k]) { // 1 2 map[i] = prefix[k].length; } } if (!map[i]) { map[i] = 0; } } } return map; } function KMP(SS, key) { //生成匹配表 [0,0,0,1,2,0] var part = getMap(key); var sslen = SS.length; var keylen = key.length; var result; var i = 0; var j = 0; for (i=0; i < SS.length; i++) { //最外层循环,主串 //子循环 for ( j = 0; j < keylen; j++) { //如果ss字符 和key 字符相等 if (key.charAt(j) == SS.charAt(i)) { //如果最后一个字符也相等, 说明匹配完成, 有结果, 跳出内循环 if (j == keylen - 1) { result = i - j; break; } else { i++; } } else { //abcabf
//00012 part[j] 是 f 的位置
//abcabd
if (j > 1 && part[j - 1] > 0) { // 已匹配的 减去 部分相等前缀长度, 最好调试一下这里
//回溯 i -= part[j-1] console.log(i) } else { //跳过已经匹配的 或者 没有匹配的 //i = i - j;
//如果有匹配的字符, 因为排除了kmp情况, 直接跳过已经匹配过的
if(j>0){
//i 其实保持不变, 因为跳出内循环后, 外循环i会+1
i -= 1;
console.log(i + 'bb')
}else{
//移动一位
i = i - j;
}
} //因为不匹配, 所以跳出本次关键字对比 break; } } //如果有结果退出当前循环 if (result || result == 0) { break; } } //如果没有结果 if (result || result == 0) { return result } else { return -1; } } var s = "abcabfababcabffffffabcabd"; var k = "abcabd"; var r = KMP(s,k) console.log(r)