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 (短字符串在第五个字符第一次匹配错误)

    ababb (比较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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值