KMP算法是字符串匹配的經典算法,簡稱 看毛片, 理論知識請直接看阮一峯老師的這篇文章,我看完文章以後嘗試對算法進行了實現。html
一句話總結KMP算法的核心思想:就是跳過已經對比的部分算法
而KMP算法的核心組成就是部分匹配表 + 回退算法。數組
部分匹配表1.0版本app
function KMPpartMatchTable(str) { var matchTable = [0]; var prefix = [], suffix = []; for(var i = 1; i < str.length; i++) { prefix = getPrefix(str.substr(0, i + 1)) suffix = getSuffix(str.substr(0, i + 1)) var ret = [0]; //默認設置一個0,防止-Infinity //對比2個數組,是否有重複的 prefix.forEach(function(n, i) { for(var j = i; j < suffix.length; j++) { if(n == suffix[j]) { ret.push(n.length) } } }) matchTable.push(Math.max.apply(null, ret)) } //生成前綴數組 function getPrefix(s) { let ret = [] for(var len = s.length; len > 0; len--) { if(len == s.length) continue; ret.push(s.substring(0, len)) } return ret.reverse(); } //生成後綴數組 function getSuffix(s) { let ret = [] for(var len = s.length; len > 0; len--) { if(len == s.length) continue; ret.push(s.substring(len, s.length)) } return ret.reverse(); } return matchTable }
這是我初版寫出來的,能夠看到2個getPrefix和getSuffix有大部分是重複的代碼。方便理解。須要for循環2次字符串,但不利於性能。因此能夠將他們進行精簡合併爲1次性能
部分匹配表2.0版本spa
function KMPpartMatchTable(str) { var matchTable = [0]; var prefix = [], suffix = []; for(var i = 1; i < str.length; i++) { // prefix = getPrefix(str.substr(0, i + 1)) // suffix = getSuffix(str.substr(0, i + 1)) var s = str.substr(0, i + 1); for(var len = s.length; len > 0; len--) { if(len == s.length) continue; prefix.push(s.substring(0, len)) //前綴數組 suffix.push(s.substring(len, s.length)) //後綴數組 } var ret = [0]; //默認設置一個0,防止-Infinity //對比2個數組,是否有重複的 prefix.forEach(function(n, i) { for(var j = i; j < suffix.length; j++) { if(n == suffix[j]) { ret.push(n.length) } } }) matchTable.push(Math.max.apply(null, ret)) } return matchTable }
KMPpartMatchTable('ABCDABD')//[0,0,0,0,1,2,0]
改進事後,邏輯沒那麼直觀了。但一次字符串for循環就生成出了前綴和後綴數組code
回退算法htm
function KMP(sourceStr, targetStr) { var partMatchValue = KMPpartMatchTable(targetStr); //拿到匹配表 var result = false; for(var i = 0; i < sourceStr.length; i++) { for(var k = 0; k < targetStr.length; k++) { if(str.charAt(k) == sourceStr.charAt(i)) { if(k == targetStr.length - 1) { result = true; break; } else { i++; } } else { if(k > 0 && partMatchValue[k - 1] > 0) { k = partMatchValue[k - 1] - 1; } else { break; } } } if(result) { break; } } return result } var ss = 'ABCDAB ABCDAB ABCDAABCABCDABDABCDABDDABDBD' var str = 'ABCDABD' console.log(KMP(ss, str)) //true