假如給定長度爲n的字符串A,以及長度爲m的字符串B,求B在A中的位置,這個位置也稱爲起始位置。本文假設:javascript
var strA = "ABBACC" var strB = "ACC"
求得strA的長度爲 la,strB的長度爲 lb,一共有(n-m)個起始位置,若是從0開始,那麼起始位置的範圍爲:0到(n-m),若是從1開始,起始位置的範圍爲1到(n-m + 1)。html
BF算法是最簡單的一種匹配方法,它主要利用雙指針進行 (n x m) 次循環,至關於暴力破解,其時間複雜度爲: O(n*m),迭代步驟以下:java
// JS第一種寫法 function getStrLoc(str, pattern) { let i = 0, j = 0; let n = str.length; let m = pattern.length; while(i < n && j < m) { for (; j < m; ) { if (str[i + j] === pattern[j]) { j++; // 判斷 j + 1 } else { j = 0; i++; break; // i 失效 } } } return (j == m) ? i : -1; } console.log(getStrLoc("ababcababd", "ababd")) // 5 console.log(getStrLoc("aabaacaaa", "aac")) // 3 console.log(getStrLoc("aabb", "bb")) // 2 console.log(getStrLoc("aaa", "aa")) // 0 // JS 第二種寫法(邏輯更清晰) function getStrLoc(str, pattern) { let i = 0, j = 0; let n = str.length; let m = pattern.length; while(i < n && j < m) { if (str[i] === pattern[j]) { i++; j++; } else { i = i - j + 1; j = 0; } } return (j == m) ? (i - m) : -1; } console.log(getStrLoc("ababcababd", "ababd")) // 5 console.log(getStrLoc("aabaacaaa", "aac")) // 3 console.log(getStrLoc("aabb", "bb")) // 2
KMP 算法的核心在於不回溯str指針,將模式字符串指針回退到合適的位置:
算法
第一種寫法:ui
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> <link rel="stylesheet" href=""> </head> <body> </body> <script> function BuildMatch(str, next) { for(let i = 0; i < str.length; i++) { var sstr = str.substr(0, i + 1); if (sstr.length < 2) { next[i] = -1 } else { var preffix = [] var affix = [] for(let index = 0; index < sstr.length; index++) { if (index === 0) { preffix.push(sstr[index]) } else if (index === sstr.length - 1) { affix.push(sstr[sstr.length - 1]) } else { preffix.push(sstr.substr(0, index + 1)) affix.push(sstr.substr(index)) } } var common = preffix.filter(ele => affix.indexOf(ele) !== -1); var max = -1 common.forEach(el => { if (el.length > max) max = el.length - 1 }) next[i] = max } } return next } function KMP(string, pattern) { var n = string.length; var m = pattern.length; var s = 0, p = 0, match = []; BuildMatch(pattern, match); console.log(match) while(s < n && p < m) { if (string[s] == pattern[p]) { s++; p++; } else if (p > 0) { // 調整p位置 p = match[p - 1] + 1; } else { // p = 0 時匹配失敗, 調整s位置 s++; } } return (p == m)? (s - m) : -1; } var str = 'aaabbbcccaaabbbccc' var pattern = 'cccaaa' var p = KMP(str, pattern) console.log(p) </script> </html>
第二種寫法:指針
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Document</title> </head> <body> <script> function BuildMatch(pattern, match) { var i, j; var m = pattern.length; match[0] = -1; for (j = 1; j < m; j++) { i = match[j - 1]; while ((i >= 0) && (pattern[i + 1] != pattern[j])) { i = match[i]; } if (pattern[i+1] == pattern[j]) { match[j] = i+1; } else { match[j] = -1; } } } function KMP(string, pattern) { var n = string.length; var m = pattern.length; var s = 0, p = 0, match = []; BuildMatch(pattern, match); console.log(match) while(s < n && p < m) { if (string[s] == pattern[p]) { s++; p++; } else if (p > 0) { // 調整p位置 p = match[p - 1] + 1; } else { // p = 0時匹配失敗, 調整s位置 s++; } } return (p == m)? (s - m) : -1; } function main() { var string = "ababcababe"; var pattern = "ababe"; var p = KMP(string, pattern); if (p == -1) { console.log("Not Found.\n"); } else { console.log("%s\n", p); } } main() </script> </body> </html>