c++ kmp算法字符匹配_字符串匹配算法总结——BF、KMP、BM

82f77438e780843fec5a705b589d34fc.png

说明

以下算法介绍中,被匹配字符串称为主串,匹配模式字符串称为匹配串,索引从0开始。

前缀数组:字符串S = AB(B !== ⏀,即B为任一非空字符串) ,S的前缀指A。前缀数组指所有包含第一个字符但不包含最后一个字符的子串集合。

后缀数组:字符串S = AB(A !== ⏀,即A为任一非空字符串) ,S的后缀指B。后缀数组指所有包含最后一个字符但不包含第一个字符的子串集合。

一、BF

Brute Force 算法,简称BF。基本思想就是将主串第pos个字符与匹配串的第一个字符进行比较,若相等,继续将主串的下一个字符与匹配串的下一个字符进行比较,若不相等,从主串的pos + 1 位置开始与匹配串的第一个字符进行比较,重复前面的比较模式,直到得到结果。

假设主串长度为N,匹配串长度为M,该算法最坏的情况下需要进行 M*(N-M) 次比较,算法时间复杂度为O(M*N)。

/**

二、KMP

Knuth-Morris-Pratt 算法,简称KMP,由 Donald Knuth,James H. Morris,Vaughan Pratt 1977年联合发表。

BF 算法效率低在于每轮主串从pos位置开始比较过程中,若字符不相等,主串会回退到 pos+1 位置,然后与匹配串第一个位置字符开始比较。KMP 算法首先算出一个next 数组,匹配串每轮匹配在 j 位置失配时,匹配串向右滑动的距离为 j - next[j]。next 数组的计算规则如下。其中 j 为匹配串索引:

1、j = 0 时,next[j] = -1;

2、j > 0,匹配串0至 j-1 位置的子串前缀和后缀数组中最长相同元素的长度为maxL,则next[j] = maxL。特殊的,当 j=1时,前缀和后缀都为空,所以maxL=0,next[1] = 0;

/**
 * @name kmp
 * @param <String> s 主串
 * @param <String> m 匹配串
 * @return <Number> pos 返回匹配串在主串第一次出现的位置,若不存在,返回-1
 */
function kmp(s = '', m = '') {
	const lenS = s.length
  const lenM = m.length
  let j = 0
  if(!lenS || !lenM || lenS < lenM) {
    return -1
  }
  const next = getNext(m)
  for(let pos = 0; pos < lenS - lenM + 1; pos++) {
    while(j < lenM && s[pos + j] === m[j]) {
      j++
    }
    // 匹配完成
    if(j === lenM) {
      return pos
    }
    // 匹配不完全,匹配串从next[j] 位置开始匹配
    j = next[j]
  }
  return -1
}

/**
 * @name next
 * @param <String> m 需要求出next数组的匹配串
 * @return <Array> 返回next 数组
 */
function getNext(m = '') {
  const len = m.length
	const next = new Array(len).fill(0)
  next[0] = -1
  // 保存前缀和后缀数组最长相同子串
  let k = 0
  for(let j = 1; j < len - 1; j++) {
    while((k > 0) && (m[j - 1] !== m[k])) {
          k = next[j-1]
    }
    if((j > 1) && (m[j - 1] === m[k])) {
      k++
    }
    next[j] = k
  }
  return next
}

二、BM

Boyer-Moore 算法,简称BM,由Robert S. Boyer 和 J Strother Moore 1977年发明。

当在 i 位置字符失配,进行下一轮匹配时,BF算法主串回退到 i+1 位置,匹配串回退到0位置。而KMP 算法主串不回退,匹配串自0位置向右滑动一定距离再进行比较。显然,匹配串向右滑动的距离越长,算法越高效。BM 算法从末尾开始匹配,根据以下两条规则,计算得出的两个数字取最大的一个为最终滑动距离:

1、坏字符规则

926b84beac2476c981fa2978d371bda5.png

如上图,匹配过程中,m 位置 j 的 c字符不等于a,所以c是坏字符。这时有两种情况:

i、如果匹配串m的坏字符c左侧位置 i(i < j)存在字符等于坏字符对应主串中的a字符,则匹配m向右滑动距离为 j-i,即将位置 i 的字符与当前坏字符对应主串的字符对齐。

ii、如果匹配串m的坏字符左侧不存在字符等于坏字符对应的主串中的字符,则m向右滑动距离为 j+1。

2、好后缀规则

18c2e38c67ff3e2876f9dc0835404d95.png

如上图,匹配串从末尾开始匹配,匹配到m中的c不等于s中的a,则c是坏字符,而"ba"的所有子串都是好后缀,设为"ab"为 t,好后缀最大的索引为 i,即a 索引为i。这时也有两种情况:

i、如果m的t左侧存在子串 t' 使得 t'===t,则滑动m使得 t‘ 与 s中的 t 对齐。如果 t' ,则查找是否在 t 的某一个后缀 t2 左侧存在子串 t3 等于t2 ,则滑动m 使得t3 与s 中对应的t2 对齐。

iii、如果匹配串m 的好后缀左侧都不存在符合第一种情况的子串,则整个匹配串向右滑动距离为 i+1。

后记

这篇文章感觉要烂尾了呀,时间线拖太长了。

很珍惜能来到tx 的机会,也很认真地想把工作做好,反而如履薄冰,战战兢兢。还是喜欢洒脱的自己,只管全力以赴,其他的,不问、不求、不急,花开花谢,都是风情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值