串的算法学习笔记

26 阅读1分钟
  1. 蛮力算法
  2. KMP算法

蛮力算法

function indexof(text, pattern) {
  if (text == null || pattern == null) return;
  let tlen = text.length;
  let plen = pattern.length;
  if (tlen === 0 || plen === 0 || tlen < plen) return -1;
  let pi = 0, ti = 0;
  while(pi < plen && ti < tlen) {
    if (text.charAt(ti) === pattern.charAt(pi)) {
      ti++;
      pi++;
    } else {
      ti -= pi - 1; // 下一轮开始比较的位置
      pi = 0;
    }
  }
  return pi == plen ? ti - pi : -1;
}

蛮力优化一: 在恰当的时候可以提前提出,较少比较次数

function indexof(text, pattern) {
  if (text == null || pattern == null) return;
  let tlen = text.length;
  let plen = pattern.length;
  if (tlen === 0 || plen === 0 || tlen < plen) return -1;
  let pi = 0, ti = 0;
  let tmax = tlen - plen;
  // 只有text剩下可比较的长度大于pattern的长度才比较,否则无意义,一定是不包含的.
  while(pi < plen && ti - pi <= tmax) {
    if (text.charAt(ti) === pattern.charAt(pi)) {
      ti++;
      pi++;
    } else {
      ti -= pi - 1;
      pi = 0;
    }
  }
  return pi == plen ? ti - pi : -1;
}

蛮力算法实现2

function indexof(text, pattern) {
  if (text == null || pattern == null) return -1;
  let tlen = text.length;
  let plen = pattern.length;
  if (tlen === 0 || plen === 0 || tlen < plen) return -1;
  let tmax = tlen - plen;
  for (let ti = 0; ti <= tmax; ti++) {
    let pi = 0;
    for (;pi < plen; pi++) {
      if (text.charAt(ti + pi) !== pattern.charAt(pi)) break;
    }
    if (pi === plen) return ti;
  }
  return -1;

}

蛮力算法性能分析
   文本串长度 m 模式串长度
  最好情况 只需一轮比较就完全匹配。比较m次,时间复杂度为o(m)
  最坏情况,执行了 n - m + 1 轮比较,每轮比较到模式串的末字符串后失败(m -1)次成功,1次失败
  时间复杂度为 o(m * (n - m + 1)) ----- o(mn)

Kmp

  KMP 算法充分利用了此前比较过的内容,可以很聪明的跳过一些不必要的比较位置

function nextFn(pattern) {
  let len = pattern.length;
  // let next = new Array(len).fill(0);
  let next = new Array(len);
  let i = 0;
  let n = next[i] = -1;
  let imax = len - 1;

  while(i < imax) {
    if (n < 0 || pattern.charAt(i) == pattern.charAt(n)) {
      next[++i] = ++n;
    } else {
      n = next[n];
    }
  }

  return next
}

function indexof(text, pattern) {
  if (text == null || pattern == null) return -1;
  let tlen = text.length;
  let plen = pattern.length;
  if (tlen === 0 || plen === 0 || tlen < plen) return -1;
  let next = nextFn(pattern);
  let pi = 0, ti = 0;
  let tmax = tlen - plen;
  while(pi < plen && ti - pi <= tmax) {
    if (pi < 0 || text.charAt(ti) === pattern.charAt(pi)) {
      ti++;
      pi++;
    } else {
      pi = next[pi];
    }
  }
  return pi === plen ? ti - pi : -1;
} 

KMP 主逻辑

  最好时间的复杂度: O(m)
  最坏时间复杂度: o(n), 不超过0(2n)

next 表的构造程度和KMP主体逻辑类似
  时间复杂度: O(m)

KMP 整体
  最好时间复杂度: O(m)
  最坏复杂度: o(n+m)
  空间复杂度: o(m)

蛮力算法 与 KMP 比较
当字符失配时

蛮力算法

  ti 回溯到左边位置
  pi 回溯到 0

KMP 算法
  ti 不必回溯
  pi 不一定回溯到0