4.3.2 KMP克努特-莫里斯-普拉特操作

算法需求

  主串S长度n,模式串P长度m,匹配情况是S串的i位置,P串的j位置失配,这个时候为了防止匹配的指针回溯和一些不必要的匹配次数,所以需要查找在S串中“失配”的位置i之前匹配到了P串中j位置k之前的串,这样就可以从k位置开始匹配了,省去了从新匹配的效果

补充:

  1. 其最终原理是采用了 在模式串中查找 前缀和后缀匹配的最长长度,用空间换时间的问题
  2. 则用【反正法】: 如果s[i] !=p[j], 则下一个开始的位置为在x~i之间移动, 那么如果接下来跟p这个模式子串匹配了,那么因为p向右移动导致了,其实跟在i之前的跟s匹配的部分反而少了,所以 如果因为移动了,并且匹配上了, 说明 s中 [x,i] 之间 肯定有一部分是和p匹配的,这个就是前缀和后缀的匹配位置,也就是说如果在【x,1】之间如果匹配的到了p,那么一定有有重合的后半部分和前半部分相匹配,如果没有那么直接跳转到p[1] 和s[i]直接开始匹配就行了,如果怕漏了,即略过了匹配的地方,那么刚刚反正的,如果有匹配的,那么一定存在前后缀的匹配,那么从前后缀匹配开始就行了,而且需要选在最大的那个后缀
算法推理
  1. 假设此时应与模式中第k(k< j)个位置

  2. 模式中前k-1个字符的字串必须满足下列关系,并且不存在k`>k这种情况
    P 1 . . . . . P k − 1 = S i − k + 1 . . . . . S i − 1 P_ {1}.....P_ {k-1} =S_{i-k+1}.....S_{i-1} P1.....Pk1=Sik+1.....Si1

  3. 且已经得到的匹配结果为
    P j − k + 1 . . . . . P j − 1 = S i − k + 1 . . . . . S i − 1 P_ {j-k+1}.....P_ {j-1} =S_{i-k+1}.....S_{i-1} Pjk+1.....Pj1=Sik+1.....Si1

  4. 综合上述的结果可以得到
    P 1 . . . . . P k − 1 = P j − k + 1 . . . . . P j − 1 P_ {1}.....P_ {k-1} =P_ {j-k+1}.....P_ {j-1} P1.....Pk1=Pjk+1.....Pj1

  5. 这个时候求得k值即可知道移动的位置

疑问

可能会疑问,确定移动出去的j-k的哪几个元素没有匹配的吗?
eg: 用上面的反正就可以知,我们求的就是最大的前缀匹配

K值求解

模式串的的next[j]值,取决于模式串的本身而和相匹配的主串无关,可以从分析其定义出发用递推的方式求的next的函数值

  1. next[1] =0

  2. 设next[j]=k,则 P 1 ⋅ ⋅ ⋅ P k − 1 = P j − k + 1 ⋅ ⋅ ⋅ P j − 1 P_{1}···P_{k-1} = P_{j-k+1}···P_{j-1} P1Pk1=Pjk+1Pj1,其中1< k < j,且不存在k` > k

  3. 那么next[j+1]就得视情况而定
    a. 当 P k = P j P_{k} = P_{j} Pk=Pj

    P 1 ⋅ ⋅ ⋅ P k = P j − k + 1 ⋅ ⋅ ⋅ P j P_{1}···P_{k} = P_{j-k+1}···P_{j} P1Pk=Pjk+1Pj,则next[j+1] = k`

    b. 当 P k ! = P j P_{k} != P_{j} Pk=Pj

    这是就相当于 P 1 ⋅ ⋅ ⋅ P j P_{1}···P_{j} P1Pj为目标字符串 P 1 ⋅ ⋅ ⋅ P k P_{1}···P_{k} P1Pk为模式字符串,这个时候问题就相当于重新求 P 1 ⋅ ⋅ ⋅ P k P_{1}···P_{k} P1Pk字符串的next[k],并比较next[k] == p[j],若果相等则next[j+1] = next[k]+1;

逻辑问题

  1. 原来是 主串中 x~i 中的前后匹配的串,但是其实是求的 模式串中0-j中匹配的前后串
  2. 因为转化为了 模式串中的各个位置的最佳匹配的串,而且前提是next[1] = 0; 题设是在 next[j] = k 且不存在k`>k的情况下的题设,且next[j+1] 依赖的是next[j]之前的已经匹配的串的情况的,所以 地推的都是最大情况
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值