从结果出发,浅显易懂的KMP解释

字符串匹配问题是什么?

给定一个主串:S

给定一个模式串:P

在 S 中找到 P 出现的位置,即字符串匹配问题。常见场景如文档搜索。

BF

分析

BF 是暴力搜索的英文简称,暴力一次容易理解,即把所有可能的情况都找到。

对于主串 S, 我们从头开始遍历,直到有S[i]==p[0],然后 S 和 P同时向后移动进行匹配,直至失败,或者 P 遍历结束。

如果失败,则从 S[i+1] 处继续匹配。

代码

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        j = 0
        for i in range(len(haystack) - len(needle) + 1):
            while j < len(needle) and haystack[i+j] == needle[j]:
                j += 1
            if j == len(needle):
                return i
            j = 0
        return -1

KMP

说 KMP 之前,有必要演示 BF 的流程,

在进行到 p[6] 的时候才失配,按照暴力匹配的思想,我们需要退回,使用 S[4] 去和 P[0] 进行匹配。 然而,无疑一直到 S[7] 之前,都是不会匹配成功的,那这样的话,能不能直接跳到 S[7] 呢?

好像不仅仅只跳到 s[7] ,而且还能跳到 s[9]。

不难发现,S[7] 和 S[8] 并不需要我们再去做匹配了。

那现在的问题就变成了当我们失配时,我们希望主串保持不动,只是改变,主串当前位置和模式串中的另一个字符去匹配。

如何去找,S[9] 该和哪一个字符进行匹配。

观察上图,S[9] 和 P[2] 进行了匹配,2 = len('AB'),相信我,这并不是一个巧合,也就是说,匹配失败时,我们希望 j 回到上一个 AB 后面。

把这两个 AB 称为最长公共前缀后缀

现在,我们要做的事情变得简单多了,找出这个最长公共前缀后缀

形如 P[K:K+N] = P[0:N]。

k 表示第 j 个位置的最长公共前缀后缀的长度

j0123456
''ABCDABD
k0000120

我们找到的这个东东称作 next,next的作用是,失配时,帮助我们回溯到一个正确的位置。

下面给出寻找 next 的代码。

实际上是一个 dp 的过程,分为两种情况

  • 匹配成功时 j + 1,赋值给next[i]

  • 匹配失败时进行回溯,回溯到上一次匹配成功的地方,j = p_next[j-1]

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        def getNext(p):
            p_next = [0] * len(p)
            j = 0
            for i in range(1,len(p)):
                while j > 0 and p[i] != p[j]:
                    j = p_next[j-1]  # 匹配失败,回到上次匹配成功的地方
                if p[i] == p[j]:
                    j += 1
                p_next[i] = j
            return p_next

KMP完整代码

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        # KMP
        if not needle:
            return 0
        
        def getNext(p):
            p_next = [0] * len(p)
            j = 0
            for i in range(1,len(p)):
                while j > 0 and p[i] != p[j]:
                    j = p_next[j-1]  # 匹配失败,回到上次匹配成功的地方
                if p[i] == p[j]:
                    j += 1
                p_next[i] = j
            return p_next
        p_next = getNext(needle)
        
        i, j = 0, 0
        while i < len(haystack) and j < len(needle):
            if haystack[i] == needle[j]:
                i += 1
                j += 1
            else:   # 匹配失败,回溯
                if j > 0: 
                    j = p_next[j-1]
                else:  # 
                    i += 1
        return i - len(needle) if j == len(needle) else -1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值