字符串匹配-KMP

论坛 期权论坛     
选择匿名的用户   2021-5-23 14:29   145   0
<div class="blogpost-body" id="cnblogs_post_body">
<p>节选自 https://www.cnblogs.com/zhangtianq/p/5839909.html</p>
<p>字符串匹配 KMP O(m&#43;n)</p>
<p>O原来的暴力算法 当不匹配的时候</p>
<p><img alt="" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-1e29dab71739ea9a3f0ead941b951d6e"></p>
<p><span style="color:#333333;font-family:Arial;font-size:14px;">尽管之前文本串和模式串已经分别匹配到了S[9]、P[5],但因为S[10]跟P[6]不匹配,所以文本串回溯到S[5],模式串回溯到P[0],从而让S[5]跟P[0]匹配</span></p>
<p> </p>
<p style="font-size:14px;color:#333333;font-family:Arial;"> 而S[5]肯定跟P[0]失配。为什么呢?因为在之前第4步匹配中,我们已经得知S[5] &#61; P[1] &#61; B,而P[0] &#61; A,即P[1] !&#61; P[0],故S[5]必定不等于P[0],所以回溯过去必然会导致失配。那有没有一种算法,让i 不往回退,只需要移动j 即可呢?</p>
<p style="font-size:14px;color:#333333;font-family:Arial;">    答案是肯定的。这种算法就是本文的主旨KMP算法,它利用之前已经部分匹配这个有效信息,保持i 不回溯,通过修改j 的位置,让模式串尽量地移动到有效的位置。</p>
<div>
   
</div>
<p><span style="color:#333333;font-family:Arial;font-size:14px;"> </span></p>
<p><span style="color:#333333;font-family:Arial;font-size:14px;"><span style="color:#333333;font-family:Arial;font-size:14px;text-align:left;">假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置</span></span></p>
<ul style="margin-left:30px;color:#333333;font-family:Arial;font-size:14px;text-align:left;"><li style="margin-left:0px;">如果j &#61; -1,或者当前字符匹配成功(即S[i] &#61;&#61; P[j]),都令i&#43;&#43;,j&#43;&#43;,继续匹配下一个字符;</li><li style="margin-left:0px;">如果j !&#61; -1,且当前字符匹配失败(即S[i] !&#61; P[j]),则令 i 不变,j &#61; next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。</li></ul>
<p><span style="color:#333333;font-family:Arial;font-size:14px;">next[j]表示模式串前j-1个字符组成的子串,前缀和后缀相同的最长的长度</span></p>
<p><span style="color:#333333;font-family:Arial;font-size:14px;"><img alt="" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-0f7faf3d04a370c849f4940453c91b68"><br></span></p>
<p><span style="color:#333333;font-family:Arial;font-size:14px;"> </span></p>
<p><span style="color:#333333;font-family:Arial;font-size:14px;"> </span></p>
<p> </p>
<p style="font-size:14px;color:#333333;font-family:Arial;">计算next 数组的方法可以采用递推:</p>
<ul style="margin-left:30px;font-size:14px;color:#333333;font-family:Arial;line-height:26px;"><li style="margin-left:0px;"><span style="font-family:&#39;Comic Sans MS&#39;;">1</span>. 如果对于值k,已有p0 p1, ..., pk-1 &#61; pj-k pj-k&#43;1, ..., pj-1,相当于next[j] &#61; k。</li></ul>
<ul style="margin-left:30px;font-size:14px;color:#333333;font-family:Arial;line-height:26px;"><li style="margin-left:0px;">2.已知next [0, ..., j],如何求出next [j &#43; 1]呢?</li></ul>
<p style="font-size:14px;color:#333333;font-family:Arial;">    对于P的前j&#43;1个序列字符:</p>
<ul style="margin-left:30px;font-size:14px;color:#333333;font-family:Arial;line-height:26px;"><li style="margin-left:0px;">若p[k] &#61;&#61; p[j],则next[j &#43; 1 ] &#61; next [j] &#43; 1 &#61; k &#43; 1;</li><li style="margin-left:0px;">若p[k ] ≠ p[j],如果此时p[ next[k] ] &#61;&#61; p[j ],则next[ j &#43; 1 ] &#61;  next[k] &#43; 1,否则继续递归前缀索引k &#61; next[k],而后重复此过程。 相当于在字符p[j&#43;1]之前不存在长度为k&#43;1的前缀&#34;p0 p1, …, pk-1 pk&#34;跟后缀“pj-k pj-k&#43;1, …, pj-1 pj&#34;相等,那么是否可能存在另一个值t&#43;1 &lt; k&#43;1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t&#43;1, …, pj-1 pj” 呢?如果存在,那么这个t&#43;1 便是next[ j&#43;1]的值,此相当于利用已经求得的next 数组(next [0, ..., k, ..., j])进行P串前缀跟P串后缀的匹配。</li></ul>
<div style="font-size:14px;color:#333333;font-family:Arial;line-height:26px;">
  <div>
   <div>
    那
    <span style="color:#ff0000;">为何递归前缀索引k &#61; next[k],就能找到长度更短的相同前缀后缀呢</span>?这又归根到next数组的含义。
    <span style="font-family:&#39;Comic Sans MS&#39;;">我们拿前缀 p0 pk-1 pk 去跟后缀pj-k pj-1 pj匹配,如果pk 跟pj 失配,下一步就是用p[next[k]] 去跟pj 继续匹配,如果p[ next[k] ]跟pj还是不匹配,则需要寻找长度更短的相同前缀后缀,即下一步用p[ next[ next[k] ] ]去跟pj匹配</span>。此过程相当于模式串的自我匹配,所以不断的递归k &#61; next[k],直到要么找到长度更短的相同前缀后缀,要么没有长度更短的相同
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP