字符串的模式匹配

1. 模式匹配方法

假如給定長度爲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

2. 樸素模式匹配 (BF算法)

BF算法是最簡單的一種匹配方法,它主要利用雙指針進行 (n x m) 次循環,至關於暴力破解,其時間複雜度爲: O(n*m),迭代步驟以下:java

  • 設B在A中的位置爲i
  • 檢測 i = 0 時,strB 與 strA 是否匹配成功
  • 檢測 i = 1 時,strB 與 strA 是否匹配成功
  • 檢測 i = 2 時,strB 與 strA 是否匹配成功
  • ......
// 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

3. KMP模式匹配

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>
相關文章
相關標籤/搜索