kmp算法总结,附上代码

本文介绍了KMP算法,一种用于高效查找字符串中模式串出现次数的方法。通过构建nxt数组,KMP算法减少了不必要的字符比较,暴力法的两层循环被优化为O(m+n)的时间复杂度。
摘要由CSDN通过智能技术生成

kmp算法总结

1. 什么是kmp算法

现有两字符串a, b, 我们需要统计后者在前者中出现的次数,kmp算法就是用来解决此类问题的

2. 暴力

显然两层for循环可以暴力求解该问题,时间复杂度为 O ( n ∗ m ) O(n * m) O(nm), n和m分别为两字符串的长度

令 s = “ABADABACA”, p = “ABA”, s称为主串, p称为模式串

暴力的做法就是枚举匹配的起点,效率较低,这时候需要kmp算法

3,kmp算法

现在来考虑如何优化暴力的算法,判断字符串是否相等的时间复杂度是没法改变的,只能一个一个字符的判断,所以我们只能设法减少判断的趟数

在这里插入图片描述

暂且忽略最下面的nxt数组,此时两字符串的前三个字符相同,暴力做法是将p后移一位,而如果是kmp算法,则会将p后移两位(显然这种做法更符合人的直觉

考虑什么时候我们可以让p直接后移几步而为无脑每次只后移一步。如果我们每次移动之后,不是从p的首个字符开始匹配,而是尽可能从p[0]之后的字符开始,这就要求p[0:i]已经匹配成功。

要实现这样的效果,需要对模式串进行预处理,引入nxt数组,定义nxt[i]表示p[0]—p[i]的这段子串的最长相同前后缀的长度

前缀:起点为p[0],不包括p[-1]的子串,即’A’, ‘AB’
后缀:终点为p[-1], 不包含p[0]的子串, 即’BA’, ‘A’
nxt[0]: p[0]无前后缀,所以nxt[0] = 0
nxt[1]: p[0]-p[1]无相同前后缀,所以nxt[1] = 0
nxt[2]; p[0]-p[2], 相同且最长前后缀为’A’, 所以nxt[2] = 1

当s[i] != p[j] 时,就可以利用nxt数组,将j = nxt[j - 1], 就不需要从头开始匹配,而是利用相同前后缀,跳过相同的部分

例如,i = 2 时,完成了一次匹配工作,暴力的做法是将 i 回调为 1, 重头开始扫描整个p, 当时kmp的做法是将 i = 3, j = nxt[2] = 1, i的数值只会增加, j 尽量不回调到p的起始位置

在这里插入图片描述

4.代码

def kmp(s, p):
    # 建立nxt数组
    nxt = [0]
    plen = 0   #当前已扫描的p子串的最长相同前后缀长度
    i = 1
    while i < len(p):
        if p[plen] == p[i]:
            plen += 1
            nxt.append(plen)
            i += 1
        else:
            if plen == 0:  # 当前子串无相同前后缀
                nxt.append(0)
                i += 1
            else: 
                plen = nxt[plen - 1]

    # 利用nxt数组进行匹配
    i, j, cnt = 0, 0, 0
    while i < len(s):
        if s[i] == p[j]:
            i += 1
            j += 1
        else:
            if j == 0:  # j 回调到起始位置,还无法匹配
                i += 1
            else:
                j = nxt[j - 1] # j 回调 

        if j == len(p): # 匹配成功
            cnt += 1
            j = nxt[j - 1] # 这里记得回调j,否则p的下标越界

    return cnt

5. 时间复杂度分析

时间复杂度分析:

  1. 建立nxt数组,扫描一次p串,回调j的次数可以忽略
  2. 利用nxt匹配,扫描一次s串,回调j的次数可以忽略

故kmp算法的时间复杂度为 O ( m + n ) O(m + n) O(m+n)

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值