KMP算法---私人笔记

  先给出模式匹配问题:给出两个字符穿,一个为S(主串)另一个为T(字串),模式匹配就是求T在S中的位置。我们先介绍简单的模式匹配算法,KMP算法是基于这种算法的改进算法。

  简单的模式匹配算法:从S的第一个字符开始和T的第一个字符进行比较,若相等,则继续逐个的比较后续的字符,直到T中的每个字符依次和S中的一个连续的字符序列相等,则匹配成功,返回这个S中的连续字符序列的第一个字符的下标,如果在比较过程中有某个字符不相等,则从S 的下一个字符开始重新和T的第一个字符比较,依此类推,直到S中的字符都比较完了仍然没有匹配成功的话,则匹配不成功。Python实现代码如下:

def StrMatching(S,T):
    i,j=0,0
    m,n=len(S),len(T)
    while i<m and j<n:
        if S[i]==T[j]:
            i+=1
            j+=1
        else:
            i=i-j+1
            j=0
    if j==n:
        return i-n
   else: return 0

  KMP算法:

  接下来进入我们的正餐KMP算法,上述的简单的模式匹配算法的最坏的时间复杂度为O(n*m),KMP算法可以在(m+n)的时间数量级上完成模式匹配操作。KMP算法的改进在于:每当一趟匹配过程中出现字符不相等时,不需要回溯i指针,而是利用已经得到的"部分匹配"的结果将模式串T向右"滑动"尽可能远的一段距离后,再继续进行比较。这样的表述比较的抽象,我的理解是:在使用KMP算法进行模式匹配时,使用了两个指针i和j,其中i为指向主串S的指针,j为指向子串T的指针。在没有遇到不相等的字符之前,i指针和j指针同时向右移动,当碰到不相等的字符的时候,保持i指针不动,把子串T向后滑动一个合适的位置(向后滑动的过程即为将j指针更新的过程),让这个位置的字符和i指针指向的字符进行比较,这个合适位置与子串本身的结构有关。根据子串和一个求取next函数值的算法可以得到一个next整数数组,这个数组的长度和子串T的长度一样,next数组中的数值对应于相同下标的子串T中的字符如果遇到不匹配的情况时j指针更新的数(在匹配过程中,如果遇到不相等的字符,则根据这个字符对应数组中的值,将j指针的值变更为这个值),这个next数组的求解实际是对子串T的每个位置找到最长的公共前缀。在得到next数组后,利用next数组就能完成模式匹配的过程了。下面来谈谈next数组的求法以及个人的理解。

  求next数组的python代码如下:

def get_next(t,next):
    i=0
    next[0]=-1
    j=-1
    while i<len(t)-1:
        if j==-1 or t[i]==t[j]:
            i+=1
            j+=1
            next[i]=j
        else:
            j=next[j]

 上述算法用文字描述如下:

  (1):next[0]=-1

  (2)后面求解每一位的next[j]的值时,根据j的前一位进行比较,令k=next[j-1]

  (3)比较t[j-1]与t[k]:

   a.如果相等,则next[j]=k+1

   b.如果不相等,令k=next[k],若k不等于-1,跳到第三步;如果next[k]==-1,则next[j]=1

个人理解:(1)next[0]取-1的原因:next[0]对应的是子串T中的首个字符,在进行匹配时,如果主串中当前字符与子串中的首个字符不匹配,则直接将主串的指针往后移动一位,然后再与子串的首个字符进行比较,而这一过程体现在代码中为指针i和指针j都加1,因此next[0]取负一的原因就是在经过j+1操作之后要使得j的值又变为0即为子串的首字符。

       (2)在求解next[j]的值时,都是根据前一位进行比较的原因:首先,我们看代码,代码中next[i]赋值的步骤是发生在i和j都自增之后,而if语句中比较的是t[i]和t[j]是否相等,因此 ,求next[i]的值就要先比较next[i-1]和next[j-1]的值,从代码中可以看出来在求解每一位next[j]的值时,要根据j 的前一位进行比较.这是从代码上分析得到的原因,而实际的原因是:因为next数组求解的实际就是对每个位置找到最长公共前缀,这个前缀指的是当前字符前面的那些字符,就比如abc这里面c字符的前缀就是ab,因此求当前的next[j]的值需要根据j的前一位进行比较。

     (3)这个第三步就是一个迭代求每个位置最长公共前缀的过程了,迭代的终止条件为next[k]为负一或者t[j-1]与t[k]相等。

  求得next数组之后,KMP算法就与简单的模式匹配算法很相似了。不同之处仅在于当匹配过程产生失败时,指针i不变,指针j退回到next[j]的位置,并重新进行比较,并且当j为0时,指针j和i同时加1.在得到next数组之后的KMP算法的python代码如下:

def KMP(S,T,next):
    i=0
    j=0
    while i<len(S) and j<len(T):
        if j==-1 or S[i]==T[j]:
            i+=1
            j+=1
        else:
            j=next[j]
    return i-j

下面给出一个在python中的KMP算法的完整例子:

def get_next(t,next):
    i=0
    next[0]=-1
    j=-1
    while i<len(t)-1:
        if j==-1 or t[i]==t[j]:
            i+=1
            j+=1
            next[i]=j
        else:
            j=next[j]
def KMP(S,T,next):
    i=0
    j=0
    while i<len(S) and j<len(T):
        if j==-1 or S[i]==T[j]:
            i+=1
            j+=1
        else:
            j=next[j]
    return i-j
S='abcabaaabaabcac'
T='abaabcac'
next=[0 for i in range(len(T))]
get_next(T,next)
pos=KMP(S,T,next)
print(pos)

程序运行结果为7

猜你喜欢

转载自www.cnblogs.com/fei102461/p/11741113.html
今日推荐