数据结构与算法 (八) KMP算法

1.概念引导       
       模式匹配:假设P是给定的子串,T是待查找的字符串,要求从T中找到与P相同的所有子串,这个问题称为模式匹配问题,P称为模式,T称为目标,如果T中存在一个或多个模式为P的子串,就给出该子串在T中的位置,称为匹配成功,否则,称为匹配失败

 2.算法原理      

    1).朴素的模式匹配

         用P中的字符依次与T中的字符串进行比较,首先从T的最左端开始比较,如果对于所有的k=0,1,3,...m-1,均有t[k]=p[k],则匹配成功,否则,必有某个K(0≤k<m)使得t[k]=p[k],此时可以将P右移一个字符,重新进行比较

    2)KMP 匹配算法

       使用KMP理由:从由于朴素的模式匹配算法,可以看出,每当本次匹配不成功时,P右移动一个字符,下一趟的比较右总是从P的第0个字符开始,而不管上一趟比较的中间结果是什么,因而回溯是不可避免的,而实际上这种回溯往往是不必要的,从而我们引出KMP匹配算法。

     KMP解决的问题:KMP匹配算法是由Knuth Morris 和Pratt提出的一种快速模式匹配算法:该算法解决了两个问题

    ①当比较出现不等时,确定下一趟比较前应该将P右移多少个字符

    ②P右移后应该从P中的那个字符开始和T中刚才比较时不等的那个字符继续比较

    KMP算法的核心:KMP算法借助于next数组,来确定当匹配过程中出现不等时,P右移的位数和开始比较的字符位置,在这个数组中。next[i] 的值只与P的前 i+1 个字符本身有关,而与目标T无关,(也就是说移动的位数P的 i 个符有关),至此可以归纳出两个公式:

     条件:P[t]与T[t] 不相等 

            若 next[i]大于等于0,应将P右移i-next[i]个字符,用P中的第next[i]个字符与T[j]进行比较

            若 next[i]等于-1,P中任何字符都不必再与T[i]比较,二应将P右移i+1个字符,P[0]和T[j+1]开始重新进行下一趟的比较

KMP 的关键是要计算next数组,而next数组具有一下性质:

性质1: next[i] 是一个整数 并且 满足 -1≤next[i]<i

性质2:如果P[i]和T[i]不相等 P[0]=T[j-i],P[i]=T[j-i+1],...P[i-1]=T[j-1] 

按照next[i]的含义,此时应将P右移i-next[i]个字符,用P[k](k=next[i]>0) 与T[j]继续比较 。

为了保证这样的比较时有效的,应有 P[0]=T[j-k],P[1]=T[j-k+1],...P[k-1]=T[j-1]

综上所述  p[0]=p[i-k],p[1]=p[i-k+1]...p[k-1]=p[i-1]

亦即 next[i]的取值应使P字符串的最左端k个字符组成的字符串和最右端K个字符组成的字符串相同

性质3:为了不丢失任何可能的成功匹配,当满足性质2的K存在多个可能的取值时,k的取值应保证P右移的位数I-next[i]=i-k最小,亦即取满足性质2的最大K

性质4:如果在P₁P₂....Pi-1的最左端和最右端不存在相同的子串(或者说仅存在相同的空子串),则K=0,亦即一旦在匹配过程中出现P[I]和T[j]比较时不相等,应将P右移i个字符,并从P[0]和T[j] 开始比较,显然 当i=0且P[0]不等于T[j] 时需将P右移i-next[i]=1个字符并从P[0]和T[j]开始比较,这表明next[0]=-1

性质5:由性质2可知。在匹配过程中出现P[i]和T[j]比较时不相等P右移i-next[i]=i-k个字符,从P[k]和T[j]开始继续比较而P中前K个字符无须再进行比较

猜你喜欢

转载自blog.csdn.net/weixin_43870026/article/details/85231877