最近在看关于算法方面的,正好看到关于KMP算法相关的部分,这里就做一个总结。
假设我们有这样的一个主串

S = 'googlgomglegoogle'

和一个子串

C = 'google'

我们现在有这样的一个需求那就是要在主串S中找到子串C出现的位置。可能马上会有很聪明的同学提出来,可以用indexOf方法啊。那我只能说这个方法不算。。。

朴素的模式匹配算法

这种算法又被称为暴力匹配算法。
也就是逐位匹配,假设主串的位置i子串的位置j,如果有位置j和位置i的字符相等的话,i++, j++。如果匹配失败,则回溯到主串的下一个位置重新逐位匹配。好的,我知道你肯定没有听明白,还是直接上代码好了。

var S = 'googlgomglegoogle'
var C = 'google'

var sPositon = 0
function violence1() {
  for (var i in C) {
    if (C.charAt(i) !== S.charAt(sPositon)) {
      sPositon += 1
      violence1()
    }
  }
  console.log(sPositon)
}

violence1()

然后就悲剧了

for (var i in C) {
           ^

RangeError: Maximum call stack size exceeded

超过最大调用堆栈大小, 递归没有终止会永远的循环下去,内存已爆。所以递归套循环还是需要谨慎。
好吧,那这样我们就改变一下。下面我写了两种实现方式

// 暴力匹配1
for (let i = 0; i < mainStr.length; i += 1) {
  for (let j = 0; j < searchStr.length; j += 1) {
    if (searchStr[j] !== mainStr[i + j]) {
      break
    } else if (searchStr[searchStr.length - 1] === mainStr[i + j]) {
      console.log(i)
    }
  }
}

// 暴力匹配2

let i = 0
let j = 0
while (i < mainStr.length && j < searchStr.length) {
  if (mainStr[i] === searchStr[j]) {
    i += 1
    j += 1
  } else {
    i += 1
    j = 0
  }
}
if (j === searchStr.length) {
  console.log(i - j)
} else {
  console.log('-1')
}

输出结果是11,还是很符合我们预期的效果的。那现在我们来分析一下这个算法的复杂度怎么样。
当然,在匹配算法中不同的输入会有不同的复杂度,最好的情况就是一开始就匹配成功。比如

S = 'googlestwo'
C = 'google'

此时的时间复杂度是O(1)
稍微差一点的情况,就是前几位的每一位都和子串的第一位不匹配,例如

S = 'abcderfgoogle'
C = 'google'

此时的时间复杂度为O(m + n), m为主串的长度,n为子串的长度。
最后我们分析最极端也就是最坏的情况也就是每一次不成功的匹配都发生在子串的最后一位,例如

S = 'googlgooglgooglgooglgooglgooglgooglgooglgooglgooglgooglgoogle'
C = 'google'

你说这气人不气人,就像炸金花的时候前两张都是红桃,最后一张突然蹦出个梅花,而且每把都这样。。。
此时的时间复杂度为O((n-m+1)m)
很显然这样的运行效率是十分低的。所以我们需要更加高效的算法-KMP模式匹配算法。
切入结束,下篇详解KMP匹配算法


wupengyu
1.8k 声望166 粉丝

写作是为了更好的思考