字符串查找-朴素模式匹配、KMP、BM

一、字符串

  1. 字符串一种特殊的顺序表,可以用数组或链表存储,一般用数组存储
  2. “good” 字符串长度为 4 但需要 5 个字节,最后一个为 ‘\0’

二、术语

  1. 字符串查找也叫模式匹配
  2. 比如在 T = “aaabbbccc” 字符串中查找 P = “bbb” 的字符串
  3. T 叫做目标串或主串,P 叫做模式串或子串

三、字符串查找算法

  1. 朴素的模式匹配算法
  2. KMP 算法
  3. BM 算法
1. 朴素的模式匹配算法
在 T = "goodgoogle" 字符串中查找 P = "google" 字符串的位置
  1. 从 T 第一个位置开始,T 与 P 前三个字符匹配成功,但第四个字符 ‘d’ 匹配失败
    在这里插入图片描述

  2. 从 T 第二个位置开始,‘o’ 与 ‘g’ 不相等,失败
    在这里插入图片描述

  3. 从 T 第三个位置开始,‘o’ 与 ‘g’ 不相等,失败
    在这里插入图片描述

  4. 从 T 第四个位置开始,‘d’ 与 ‘g’ 不相等,失败
    在这里插入图片描述

  5. 从 T 第五个位置开始,剩下字符全部匹配,成功
    在这里插入图片描述

  6. 朴素的模式匹配算法时间复杂度分析:
    假设目标串 T 长度为 n,模式串长度为 m,假设 T 中 存在 P

    1. 最好情况:O(m) 如 “googlegood” 中查找 “google”
    2. 最坏情况:O((n - m + 1) * m) 如在 ‘‘000…0001’’ 中查找 “001”,每次不匹配发生在串 P 的最后一个字符处
    3. 平均情况:O(n + m) 每次不匹配发生在串 P 的首字母处,最好为 m, 最坏为 n,平均为 (n + m) / 2,为 O(n + m)
  7. 缺点:目标串 T 需要回溯,比如 “goodgoogle” 匹配 “google”

2. KMP 算法
  1. 目标串 T 一直向右前进,不会向左回溯,是精确的模式匹配算法
  2. 如何避免回溯?利用模式串 P 自身的重复模式

例1: 模式串 P 无重复模式
在这里插入图片描述
例2 模式串 P 有重复模式
在这里插入图片描述

当 T[i] ≠ P[j] 时,下一次该比较 T 和 P 的哪两个字符?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 在 P 的第一个字符不匹配时,下一次匹配 T[i+1] 和 P[1],case2
  2. 若 P 成功匹配的子串没有重复模式,下一次匹配 T[i] 和 P[1],case1
  3. 若 P 成功匹配的子串有重复模式,下一次匹配 T[i] 和 P[ next[j] ],case3 case4

2.1 什么是 next 数组?

  1. next 数组表示下一次从 P 的第几个字符开始比较,P 下标从 1 开始,表示从第一个字符开始比较
    在这里插入图片描述
  2. 把 T 串各个位置的 j 值的变化定义为一个数组 next,next 长度为 T 串的长度
  3. next[j] 定义
    在这里插入图片描述
2.2 KMP 算法的主要思想
  1. 将模式串 P 的重复模式保存在 next 数组中,匹配失败时用 next 数组计算下一次 P 比较字符的位置
  2. 若 T[i] ≠ P[j] ,下一轮比较过程:
    1. 若 next[j] ≠ 0,匹配 T[i …] 与 P[ next[j] …]
    2. 若 next[j] = 0,匹配 T[i …] 与 P[1 …]
  3. 可见目标串 T 一直向右前进,不会向左回溯
2.3 计算 next 数组

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 next 数组应用

在这里插入图片描述
在这里插入图片描述

2.5 KMP 改进
  1. 若 T[i] ≠ P[j] 失配了,同时 P[j] = P[ next[j] ],此时右移 P 还是失配的,因为 T[i] ≠ P[ next[j] ]
  2. 使用 nextval 数组替代 next 数组
  3. nextval 计算规则:
    nextval[1] = 0
    for (j > 1; j <= n; j++)
    	if P[j] == P[next[j]] then nextval[j] = nextval[next[j]]
    	if P[j] != P[next[j]] then nextval[j] = next[j]
    
    简单记忆:相同用自己的 nextval[next[j]],不同照写上面 next[j]
    
2.6 nextval 示例

在这里插入图片描述

2.7 nextval 应用

在这里插入图片描述

2.8 KMP 总结
  1. 时间复杂度为 O(n + m),O(n) 表示比较时间,O(m) 表示计算 next 数组时间
  2. 目标串不出现回溯
  3. 目标串每个字符比较 1-2 次
  4. 模式串有重复模式时,KMP 才比朴素模式匹配快,因为next 数组 可以跳过一些字符
  5. 当每次失配发生在模式串第一个字符时,KMP 退化成朴素模式匹配
3. BM 算法
  1. 一种精确的字符串匹配算法
  2. 模式串匹配方向:从右到左,不同于 KMP 的从左到右
  3. 匹配过程:每次 T 与 P 右对齐,从右到左开始匹配,若发生失配,确定下一次 T 的开始位置并与 P 对其,重复该过程
  4. 关键是如何确定下一次 T 的开始位置,即目标串查找指针的移动距离,或者说目标串中指针向右跳跃的距离 dist
3.1 辅助概念-坏字符和好后缀
  1. 失配时 T[i] ≠ P[j],此时坏字符 x = T[i],好后缀 P’ = P[(k+1) … (len( P) - 1)],后缀即为已匹配的部分字符串
  2. 如何计算失配时,目标串查找指针跳跃距离 dist[i]
    1. 跳过字符(坏字符规则)CharJump[x],根据坏字符 x 计算 T 中 dist[i]
    2. 重复模式(好后缀规则)MatchJump[k],根据 P 中失配位置 k 计算 dist[i]
3.2 BM 算法思想

在这里插入图片描述

  1. 好后缀三种情况

    1. 好后缀 u 重复出现在 P
      在这里插入图片描述

    2. 好后缀 u 部分出现在 P 中
      在这里插入图片描述

    3. 好后缀 u 没有出现在 P 中
      在这里插入图片描述

  2. 坏字符两种情况

    1. 坏字符 x 出现在模式串 P 中
      在这里插入图片描述

    2. 坏字符 x 没有出现在 P 中
      在这里插入图片描述

3.3 CharJump 数组
  1. 坏字符 x 出现在 P 中
    在这里插入图片描述
    在这里插入图片描述
  2. 坏字符 x 没有出现在 P 中
    在这里插入图片描述
    在这里插入图片描述
3.4 坏字符规则

在这里插入图片描述

  1. CharJump 计算
    在这里插入图片描述
    在这里插入图片描述
  2. 坏字符应用
    在这里插入图片描述
    未完待续。。。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值