KMP算法字符串匹配,详细的next数组思想解释和代码实现,python

这篇讲解不包含kmp最基本的讲解,也不包含朴素方法对比和详细解释,主要是针对next数组构建和字符串匹配时候的指针跳转思想细节,为什么匹配失败时需要赋值j = next[j-1],防止绕不明白。

字符串匹配失败跳转方式

首先需要了解next数组的值的用法和跳转理由:
举个🌰,如下图,上面的是原串haystack,下面的是需要匹配的子串needle,匹配到第5个字符时,haystack[i]≠needle[j],此时需要查看子串needle的next数组。
在这里插入图片描述
⚠️需要查看的是j之前的串部分,是否存在前缀后缀相同的情况,例如上图存在一个前缀后缀相同(为a),所以最长相同前后缀为1,为什么我们要知道最长相同前后缀?记住这个1,之后会解释。

我们继续看上面的例子,KMP算法在于遇到不同的字符,不用再从原串haystack的下一个字符(这个例子里为i=1时的b)起从头开始匹配needle,而是利用相同前后缀,直接让匹配子串needle梭到下图所示的地方:
在这里插入图片描述
如图,i指向的位置不变,j指向1,所表示的意思就是图片这样,像是让needle直接往后梭了三格,直接让自己的前缀和haystack之前匹配完成部分的后缀对齐,这样就不用重新查看他们是否匹配了。

之前发生不匹配的地方(haystack中 i=4,needle中 j=4 的地方) 之前的部分是匹配相同的(haystack中 i<4 和 needle中 j<4处)。所以只需要计算needle的最长相同前后缀(next数组)就能知道需要梭多少格,也就是j应该重新指向哪里。

上面例子我们说过,当时的最长相同前后缀是1,为什么我们要知道最长相同前后缀?看看j重新指向的地方,就是1,这是因为在匹配失败时,我们查看失败处前一个位置的next数组对应的值,这个值记录的是最长相同前后缀的长度,参照上面的例子,重新梭到的地方(j的新值)就是最长相同前后缀中前缀后面一个位置,因此 j 的新值和最长相同前后缀的长度相等

创建next数组

初始化: next = [0]*len(pattern), next[0]=0, j=0, i=1
步骤1:i=1, j=0, 此时pattern[i]≠pattern[j],直接赋值next[i]=j=0,所以next[1]=0:
在这里插入图片描述
步骤2: i=2, j=0, 此时pattern[i]≠pattern[j],直接赋值next[i]=j=0,所以next[1]依然等于0:
在这里插入图片描述
步骤3: i=3, j=0, 此时pattern[i]=pattern[j],先让j = j+1 = 1,然后赋值next[i]=j=1,更新的 j 用红色标明:
在这里插入图片描述
步骤4: i=4, j=1, 此时pattern[i]=pattern[j],先让j = j+1 = 2,然后赋值next[i]=j=2,更新的 j 用红色标明:
在这里插入图片描述
步骤5: i=5, j=2, 此时pattern[i]=pattern[j],先让j = j+1 = 3,然后赋值next[i]=j=3,更新的 j 用红色标明:
在这里插入图片描述
步骤6: i=6, j=3, 此时pattern[i]=pattern[j],先让j = j+1 = 4,然后赋值next[i]=j=4,更新的 j 用红色标明:
在这里插入图片描述
步骤7: i=7, j=4, 此时pattern[i]≠pattern[j],此时j>0,j需要首先跳转到next[j-1]的值,此时next[j-1] = next[3]=1,所以赋值 j=4,更新的 j 用红色标明:
在这里插入图片描述
📌 这里的跳转和之前的haystack、needle的匹配失败跳转是一个意思,也是为了减少匹配的次数,同样是跳转到next[j-1],也就是 j 跳转到前方子串的最长前缀后缀长度所指向的位置,也就是这里的:next[j-1]=next[3]=1。

所以,最长前缀后缀的思想其实在”字符匹配题“中利用了两次,一次在next数组的构建中,pattern串自己和自己匹配,一次在原串和匹配子串的过程中。

此时仍然pattern[i]≠pattern[j],且j>0,所以j需要继续跳转到next[j-1]的值,此时next[j-1] = next[0] = 0,所以赋值 j=0,更新的 j 用红色标明:
在这里插入图片描述
此时仍然pattern[i]≠pattern[j],但是j不大于0,所以赋值next[i] = j = 0;
步骤8: i=8, j=0, 此时pattern[i]≠pattern[j],且j不大于0,直接赋值next[i]=j=0,结束:
在这里插入图片描述

next数组构建的代码:

def next_arr(self, pattern: str):
    n = len(pattern)
    next_array = [0]*n  # next_array存放next数组
    j = 0
    for i in range(1,n):
        while j>0 and pattern[i]!=pattern[j]: # 循环查看前缀
            j = next_array[j-1]
        if pattern[i]==pattern[j]:
            j += 1
        next_array[i] = j
    return next_array

字符串匹配代码:

def strStr(self, haystack: str, needle: str) -> int:
        m = len(haystack)
        n = len(needle)
        next = self.next_arr(needle)
        j = 0
        for i in range(m):
            while j>0 and haystack[i]!=needle[j]:
                j = next[j-1]
            if haystack[i]==needle[j]:
                j += 1
            if j == n: # 子串数到头了
                return i-j+1
        return -1
  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值