3.2 Implement strStr()

穷举法

/*
Implement strStr().
Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack

要求找到子字符串在字符串中第一次出现的位置。
相当于JS的indexOf函数
分析:

*/

// 穷举法
const strStr = function (haystack, needle) {
  if (needle.length === 0) {
    return 0;
  }
  const m = haystack.length;
  const n = needle.length;

  if (m < n) {
    return -1;
  }

  // i是 haystack跟needle比较的位置 
  // (m - n)是 剩余可比较的位置
  for (let i = 0; i <= m - n; i++) {

    // j是 needle参数 里面字符的比较位置
    for (let j = 0; j < n; j++) {
      // 有一个未匹配则进行haystac的下一轮比较
      if (haystack[i + j] !== needle[j]) {
        break;
      }

      /*
      n是 needle参数的长度,j是needle比较位置
      两者一致,表示全匹配到,返回位置i
      */
      if (j == n) {
        return i;
      }
    }
    return -1;
  }
}

KMP字符串匹配法

穷举匹配优化场景1

3.2 Implement strStr()_第1张图片
穷举匹配优化场景1

如果我们知道模式中a和后面的是不相等的,那么第一次比较后,发现后面的的4个字符均对应相等,可见a下次匹配的位置可以直接定位到f了。

穷举匹配优化场景2
3.2 Implement strStr()_第2张图片
穷举匹配优化场景2

注:场景2包括场景1。
由于abc 与后面的abc相等,可以直接得到红色的部分。而且根据前一次比较的结果,abc就不需要比较了,现在只需从f-a处开始比较即可。

获取部分匹配值

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。


3.2 Implement strStr()_第3张图片
部分匹配值
// 获取部分匹配值
function kmpGetPartMatchTable(targetStr) {
  var aPartMatchTable = [];
  var tmpCompareLen = 0;
  var tmpPartMatchVal = 0;
  var prefix, suffix;//匹配串前缀,后缀
  for (var i = 0, j = targetStr.length; i < j; i++) {
    if (i == 0) {
      aPartMatchTable[i] = 0;
      continue;
    }
    tmpCompareLen = i; //匹配串前缀,后缀最大长度
    tmpPartMatchVal = 0;
    for (; tmpCompareLen > 0; tmpCompareLen--) {
      prefix = targetStr.substr(0, tmpCompareLen);
      suffix = targetStr.substr(i - tmpCompareLen + 1, tmpCompareLen);
      if (prefix == suffix) { //找到匹配串前缀,后缀最长的共有元素
        tmpPartMatchVal = prefix.length; //部分匹配值为:匹配串前缀,后缀最长的共有元素的长度
        break;
      }
    }
    aPartMatchTable[i] = tmpPartMatchVal;
  }
  return aPartMatchTable;
}
一个完整的了KMP字符串匹配算法

穷举匹配优化思路加上部分匹配值就形成了KMP字符串匹配算法

// kmp字符串匹配
function kmpGetPartMatchTable(targetStr) {
  var aPartMatchTable = [];
  var tmpCompareLen = 0;
  var tmpPartMatchVal = 0;
  var prefix, suffix;//匹配串前缀,后缀
  for (var i = 0, j = targetStr.length; i < j; i++) {
    if (i == 0) {
      aPartMatchTable[i] = 0;
      continue;
    }
    tmpCompareLen = i; //匹配串前缀,后缀最大长度
    tmpPartMatchVal = 0;
    for (; tmpCompareLen > 0; tmpCompareLen--) {
      prefix = targetStr.substr(0, tmpCompareLen);
      suffix = targetStr.substr(i - tmpCompareLen + 1, tmpCompareLen);
      if (prefix == suffix) { //找到匹配串前缀,后缀最长的共有元素
        tmpPartMatchVal = prefix.length; //部分匹配值为:匹配串前缀,后缀最长的共有元素的长度
        break;
      }
    }
    aPartMatchTable[i] = tmpPartMatchVal;
  }
  return aPartMatchTable;
}

function KMP(sourceStr, targetStr) {
  var partMatchValue = kmpGetPartMatchTable(targetStr); //部分匹配表
  var result = -1;
  var i, j, m, n;
  n = targetStr.length;
  for (i = 0, j = sourceStr.length; i < j; i++) {
    for (var m = 0; m < n; m++) {
      if (targetStr.charAt(m) != sourceStr.charAt(i + m)) {
        if ((m > 0) && (partMatchValue[m - 1] > 0)) {
          i += (m - partMatchValue[m - 1] - 1); //设置外层循环开始位置
        }
        break;
      }
    }
    if (m == n) {
      result = i;
      break;
    }
  }
  return result;
}
const s = "BBC ABCDAB ABCDABCDABDE";
const t = "ABCDABD";
console.log('str', JSON.stringify(kmpGetPartMatchTable('ABCDABD')), KMP(s,t));

参考链接:
http://www.cnblogs.com/houkai/p/3978550.html
http://kb.cnblogs.com/page/176818/

你可能感兴趣的:(3.2 Implement strStr())