KMP算法-字符串匹配-20200914

问题描述

给定一个字符串(主串),在该字符串中查找并定位任意给定字符串(模式串)。查看给定的字符串是否包含在该字符串中。若匹配成功,则返回模式串第一个字符在主串中的位置,否则返回-1。
引自《ACM-ICPC基本算法》 滕国文,李昊编著

问题分析

字符串中的每个字符依次和主串中的一个连续的字符序列相等,则称匹配成功,反之成为匹配不成功。从主串 s 的第一个字符开始和模式串 p 的第一个字符进行比较,若相等,则继续比较两者的后续字符;若不相等,则从主串 s s s 的第二个字符开始和模式串 p p p 的第一个字符进行比较,重复上述过程,若 p p p 中的字符全部比较完毕,则说明本趟匹配成功;若主串 s s s 中剩下的字符不足以匹配整个模式串,则匹配失败。
 
在进行字符串匹配的过程中,当某个位置匹配不成功的时候,应该从模式串的下一位置开始新的比较。将这个位置的值存放在 n e x t next next 数组中,其中 n e x t next next 数组中的元素满足这个条件: n e x t next next[ j j j]= k k k,表示的是当模式串中下标为 j j j 的字符(第 j + 1 j+1 j+1 个字符)发生匹配不成功的情况时,应该从模式串的下标为 k k k 的字符(第 k + 1 k+1 k+1 个字符)开始新的匹配。如果已经得到了模式串的 n e x t next next 数组,匹配可如下进行:令指针 i i i 指向主串 s s s, 指针 j j j 指向模式串 p p p 中当前正在比较的字符的位置。对指针 i i i 和指针 j j j 指向的字符进行比较,若两字符相等,则顺序比较后面的字符;如果不相等,则指针 i i i 不动,回溯指针 j j j,令其指向模式串 p p p 的下标为 p o s pos pos 的字符, 使 p [ 0 ∼ p o s − 1 ] = s [ i − p o s ∼ i − 1 ] p[0\sim pos-1] = s[i-pos \sim i-1] p[0pos1]=s[iposi1]。然后指针 i i i 和 指针 j j j 指向的字符按此种方法继续比较,直到 j = = len ( p ) j == \text{len}(p) j==len(p),即在主串 s s s 中找到模式串 p p p 为止。获取 n e x t next next 函数的编写为整个算法的核心。
 
利用递推思想来设计 n e x t next next 函数:

  1. n e x t [ 0 ] = − 1 next[0] = -1 next[0]=1,方便编写程序,直接将 i , j i,j ij 均递增即可;
  2. 假设 n e x t [ j ] = k next[j] = k next[j]=k,说明 p [ 0 ∼ k − 1 ] = = p [ j − k ∼ j − 1 ] p[0 \sim k-1] == p[j-k \sim j-1] p[0k1]==p[jkj1]
  3. 现在来求 n e x t [ j + 1 ] next[j+1] next[j+1]
  • p [ j ] = = p [ k ] p[j]==p[k] p[j]==p[k] 时,说明 p [ 0 ∼ k ] = = p [ j − k ∼ j ] p[0\sim k] == p[j-k \sim j] p[0k]==p[jkj],这时分为两种情况讨论:当 p [ j + 1 ] ! = p [ k + 1 ] p[j+1] != p[k+1] p[j+1]!=p[k+1] 时,显然 n e x t [ j + 1 ] = k + 1 next[j+1] = k + 1 next[j+1]=k+1;当 p [ j + 1 ] = = p [ k + 1 ] p[j+1] == p[k+1] p[j+1]==p[k+1] 时,说明 p [ k + 1 ] p[k+1] p[k+1] p [ j + 1 ] p[j+1] p[j+1] 一样,都不和主串的当前字符相匹配,因此 令 m = k + 1 , m = n e x t [ m ] m=k+1,m= next[m] m=k+1m=next[m],直到 p [ m ] ! = p [ j + 1 ] p[m] != p[j+1] p[m]!=p[j+1],最终 n e x t [ j + 1 ] = m next[j+1] = m next[j+1]=m
  • p [ j ] ! = p [ k ] p[j] != p[k] p[j]!=p[k] 时,必须在 p [ 0 ∼ k − 1 ] p[0 \sim k-1] p[0k1] 中找到 n e x t [ j + 1 ] next[j+1] next[j+1],这时令 k = n e x t [ k ] k=next[k] k=next[k],直到 p [ j ] = = p [ k ] p[j] == p[k] p[j]==p[k] 或者 k = = − 1 k == -1 k==1,然后令 n e x t [ j + 1 ] = k + 1 next[j+1]=k+1 next[j+1]=k+1;这样,我们就通过递推思想求得了模式串 p p p n e x t next next 数组。

复杂度分析

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n) m m m 为 模式串 p p p 的长度, n n n 为 主串 s s s 的长度,为 p p p 构建 n e x t next next 数组复杂度为 O ( m ) O(m) O(m),在主串 s s s 中查找 p p p 复杂度为 O ( n ) O(n) O(n),因为 i i i 会一直递增。
  • 空间复杂度: O ( m ) O(m) O(m):存储模式串 p p p n e x t next next 数组。

代码

# kmp
def getNext(p):
    n = len(p)
    next = [0] * n
    next[0] = -1
    i = 0
    j = -1
    while i < n-1:
        if j == -1 or p[i] == p[j]:
            i += 1
            j += 1
            if p[i] == p[j]:
                next[i] = next[j]
            else:
                next[i] = j
        else:
            j = next[j]
    return next


def kmp(s, p):
    fail = getNext(p)
    n, m = len(s), len(p)
    i = 0
    j = 0
    while i < n:
        while not (j == -1 or s[i] == p[j]):
            j = fail[j]
        i += 1
        j += 1
        if n - i < m - j:
            break
        if j == m:
            return i-m
    return -1


if __name__ == '__main__':
    ans = kmp('abcabd', 'ca')  # 2
    pass
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

silenceagle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值