急速经典KMP算法python实现

一步步理解KMP算法

      • KMP算法简单介绍
      • 暴力匹配方式
      • KMP算法
        • KMP算法小例子
      • next原理
        • 代码

KMP算法简单介绍

KMP是三位大牛:D.E.Knuth、J.H.Morris和V.R.Pratt同时发现的。其中第一位就是《计算机程序设计艺术》的作者!!
算法主要的作用就是匹配字符串,我们设置T为主串,P为子串。配置在T中是否包含P的话,我们当然可以通过暴力匹配的方式来实现。python代码实现非常简单如下。

暴力匹配方式

def naive_match(s, p):
    m = len(s); n = len(p)
    for i in range(m-n+1):#起始指针i
        if s[i:i+n] == p:
            return True
    return False

KMP算法

算法最最重要的就是找到前后缀最长公共元素,以‘ABCDABD’为例:

  • "A"的前缀和后缀都为空集,共有元素的长度为0;
  • "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
  • "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
  • "ABCD"的前缀为[A, AB,ABC],后缀为[BCD, CD, D],共有元素的长度为0;
  • “ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A”,长度为1;
  • “ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB”,长度为2;
  • "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD,DABD, ABD, BD, D],共有元素的长度为0。
    则最终我们的‘ABCDABD’前后缀匹配值为[0,0,0,0,1,2,0]

KMP算法小例子

急速经典KMP算法python实现_第1张图片

  1. 设T为‘DABABC’,P为‘ABAC’
  2. ABAC的前后缀值为[0,0,1,0],所以保持j不变k变为P的1位置
  3. 那如何理解next呢?

next原理

急速经典KMP算法python实现_第2张图片
1.由"next[j] == k;"这个条件,我们可以得到A1子串 == A2子串(根据next数组的定义,前后缀那个)。
2.由 “next[k] == 绿色色块所在的索引;” 这个条件,我们可以得到B1子串 == B2子串。
3.由 “next[绿色色块所在的索引] == 黄色色块所在的索引;” 这个条件,我们可以得到C1子串 == C2子串。
4.由1和2 (A1 == A2,B1 == B2) 可以得到 B1 == B2 == B3。
5.由2和3 (B1 == B2, C1 == C2) 可以得到 C1 == C2 == C3
6.B2 == B3可以得到C3 == C4 == C1 == C2

  1. 如果j位置T[i]==P[j] ,则next[j]=k+1
  2. 如果不匹配,那么就要找到更小的前后缀子串,也就是A1的前缀与A2的后缀部分哪些部分是匹配的。
  3. A1==A2 推断出要找的是A1的前后缀那部分是匹配的,那么只需找到next(k)即可
  4. 就是在不匹配的情况下,令k = next(k),并迭代即可。

代码

def get_next(P):
    next_list = [0 for i in range(len(P))]
    j,k,next_list[0] = 0,-1,-1
    while j<len(P)-1:
        if k ==-1 or next_list[j] == next_list[k]:
            j += 1
            k += 1
            next_list[j]=k
        else:
            k = next_list[k]
    return next_list

def my_kmp(T,P):
    T_len = len(T)
    P_len = len(P)
    next_P = get_next(P)
    #主串位置为i,模式串为j
    i,j = 0,0
    while i< T_len and j < P_len:
        if j == -1 or T[i]==P[j]:
            i +=1
            j+=1
        else:
            j = next_P[j]
    if j == P_len:
        return  i-j

    else:
        return -1

if __name__ == '__main__':

    T = 'adsaafaaffdsaf'
    P = 'afaaf'
    print(get_next(P))
    print(my_kmp(T,P))

你可能感兴趣的:(python)