KMP算法

算法简介

KMP算法即字符串匹配算法,主要解决在一个主串S中找到子串T的起始位置。
该算法是三位大牛:D.E.Knuth、J.H.Morris和V.R.Pratt同时发现的,以其名字首字母命名。

算法引入

在学习KMP算法前,我们应该先了解前缀函数
简单来说,前缀函数是一个长度为n+1的列表next[i],存储的是子串(1~i)的最长真前缀长度。

最长真前缀:该前缀既是字符串的前缀,同时也是字符串的后缀。
例:ABCBA 最长真前缀为1,即 $A-BCB-A$
ABCAB 最长真前缀为2,即 $AB-C-AB$

注意,不存在第0个字母,所以next[0]是无意义的,为了方便遍历,我们规定next[0]=-1
例:ABCABCD 的前缀函数为 [-1,0,0,0,1,2,3,0]
AABAAAB 的前缀函数为 [-1,0,1,0,1,2,2,3]

理解了前缀函数,我们先上它的实现代码。

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

流程介绍:
· 以 j = 1 到 j = n 的顺序计算前缀函数next[j]的值。
· 为了计算 next[i] 的值,我们令 k 表示右端点位于 j 的最长真前缀长度,即 k 表示字串(1~i)的最长真前缀长度。
· 通过比较 s[j] 和 s[k] 来检查 j 的后缀是否同时也是前缀。如果两者相等,我们让 next[j]=k;不相等,我们让 k=next[k],并重复此过程。
· 初始时k=-1,我们让next[j]=0,并移至j+1。

因为next[k]记录的是子串(1~k)的最长真前缀长度,当不匹配时,我们可以直接将k回溯到 next[k] ,即回溯到该点最长真前缀长度,减少不必要的遍历

KMP算法

学习完前缀函数,我们首先计算出配对串t的前缀函数,然后进行配对。
其中 i-m+1 为字串 t 在字串 s 中第一次出现的位置

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

当计算字串 t 在字串 s 中所有出现的位置,可以将代码修改为:

def KMP(s,t):
    i = 0
    j = 0
    n = len(s)
    m = len(t)
    while i < n and j < m:
        if j == -1 or s[i] == t[j]:
            i+=1
            j+=1
        else:
            j = next1[j]
        if j == m:
            ans.append(i-m+1)
            if i + m <= n:       
                i -= next1[j]    # i应回溯最大真前缀长度
                j = 0

最后,KMP算法其实很简单,就是有点绕,实在不行就背一背板子叭。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值