1. 程式人生 > >KMP演算法(python實現)

KMP演算法(python實現)

1. 名詞和定義

字串:strs,例如'aabcaab'

字首:'a'或'aabc'等strs[0:k], k<len(strs)

字尾:strs[i:len(strs)], i>0

注:字首和字尾的最大長度是要小於strs的長度的

next陣列:next[0]=0, next[1] : strs[0:1](也即是strs的第一個元素)的字首和字尾公共元素的最大長度=0

                next[k]: strs的前k個元素組成字串的字首和字尾公共元素的最大長度

next[0]next[1]next[2]next[3]next[4]next[5]next[6]next[7]
00100123

2. next陣列的性質和實現

(1)next[0] = 0恆成立,next[i] : 表示i個元素的字串的字首和字尾公共元素的最大長度(和我們表示陣列的方式不同,next從1開始,才有意義)

(2)len(next) = len(strs)+1(也有資料說next是不要最後一項的)

(3)若i表示字串字元下標,則:令j = next[i], 若 str[i] == str[j] 則,next[i+1] = next[i] + 1; 否則 j = [next[next[i]]], 直到等式成立。根據這個可以求出next的值。程式碼如下:

def getNextList(strs):
    n = len(strs)
    nextlist = [0,0]
    j=0
    for i in range(1,n):
        while j>0 and strs[i]!=strs[j]:
            j = nextlist[j]
        if strs[i] == strs[j]:
            j += 1
        nextlist.append(j)
    return nextlist

3. KMP演算法

在長字串s中找到短字串p的位置。暴力匹配法的思想是: 找到s中第一個與p[0]相等的字元位置i,然後依次比較s[i:len(p)+i]與p,如果有一個字元不等,則返回i+1的位置,與p[0]比較;然後重複上述過程,直到遍歷完整個s。

KPM演算法與上述演算法區別就是上面紅字部分。如果s[i] = p[0],但是s[i+k] != p[k], 這時,比較s[i+k: ]和p[next[k]: ]的值(相當於p右移了k-next[k]位)。如果相等,則返回i+k-next[k],否則重複上述過程。

ababaababbc (長字串s)

ababb (短字串在第五個字元第一次匹配錯誤)

    ab

abb(比較p[2: ]和s[4: ],一直進行下去)

def KMP(s,p):
    '''
    :param s: 原始字串
    :param p: 需要匹配的字串
    :return: 匹配的位置向量
    '''
    n = len(s)
    m = len(p)
    next_list = getNextList(p)
    res = []
    j = 0
    for i in range(n):
        while s[i] != p[j] and j > 0:
            j = next_list[j]

        if s[i] == p[j]:
            j += 1
            if j == m:
                res.append(i-m+1)
                j = next_list[j]
    return res

4. next的優化

考慮下面情況:

abacaddaaa (原字串s)

abab  (需要匹配的字串p,第一次在i=3處匹配失敗,右移i-next[3]=2個單位)

    abab (比較p[next[3]]=p[1]和'c',也匹配失敗)

實際上,因為p[3] = p[next[3]],所以當p[3]匹配失敗時,p[next[3]] 肯定也匹配失敗,因此可以將next陣列按照下述規則進行優化:

if p[j] == p[next[j]]:  next[j] = next[next[j]]  對於任意的j>=1

def getNextList(strs):

    n = len(strs)
    alist = [0,0]
    k = 0

    for i in range(1,n):

        while strs[i] != strs[k] and k != 0:
            k = alist[k]

        if strs[i] == strs[k]:
            k += 1
        if strs[i] == strs[alist[i]]:   #優化新增的程式碼
            alist[i] = alist[alist[i]]
        alist.append(k)

    return alist