LeetCode28:找出字符串中第一个匹配项的下标题解之KMP算法


前言

  本文主要是以 LeetCode28:实现strStr() 为例,为大家带来KMP算法的介绍,若有表述不当的地方,望指出。此外,本文所涉及的代码语言为Python


一、LeetCode28:实现 strStr()

题目要求及示例

  实现 strStr() 函数。给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串出现的 第一个位置(下标从 0 开始)。如果不存在,则返回 -1

  • 示例1:
输入:haystack = "hello", needle = "ll"
输出:2
  • 示例2:
输入:haystack = "aaaaa", needle = "bba"
输出:-1

二、题目解法

2.1 暴力求解

  遍历字符串haystack,遍历要求是:找出haystack中与needle长度相等的所有子串;把遍历得到的每一个子串都与字符串needle进行比较,若相等则证明找到了,返回i,若循环结束仍未找到,返回-1

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        n, m = len(haystack), len(needle)
        for i in range(n):
            if haystack[i:i+m] == needle:
                return i
        return -1

2.2 KMP算法求解

2.2.1 算法原理

  KMP算法主要用于解决字符串匹配问题,算法中涉及了一个小知识点是前缀表,它依赖于前缀表从而使得在不配的时候返回到适当的位置,避免从头开始。

1、什么是前缀表?

  拿一个经典的案例来讲,这里有一个文本串:aabaabaaf,一个模式串:aabaaf,目的是从文本串中找到模式串,并返回在文本串中找到模式串的第一个位置。前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
  在讲解前缀表之前,我们先去理解三个概念:前缀、后缀以及最长相等前后缀。

  • 前缀:只包含首字母不包含尾字母的所有子串,如 aabaaf 的前缀有:a、aa、aab、aaba、aabaa
  • 后缀:只包含尾字母不包含首字母的所有子串,如 aabaaf 的后缀有:f、af、aaf、baaf、abaaf
  • 最长相等前后缀:子串中前缀和后缀相等的最大值,如aabaaf 的最长相等前后缀见下表:
子串最长相等前后缀注释
a0没有前后缀
aa1前缀是a,后缀是a,最长相等是1
aab0前缀是 a、aa、后缀是:b、ab,无相等为0
aaba1前缀是a、aa、aab,后缀是a、ba、aba,最长相等是1
aabaa2前缀是a、aa、aab、aaba,后缀是a、aa、baa、abaa,最长相等是2
aabaaf0同理
  • 前缀表:最长相等前后缀,即字符串aabaaf的前缀表是:[0 1 0 1 2 0] 。
2、前缀表的代码实现
<1> 前缀表与next数组

  其实next数组就是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。这并不涉及KMP的原理,只是遇见冲突不同的解决方法而已。本文中我们的next数组是前缀表统一右移一位之后的样子,遇见冲突时我们去找当前冲突的位置下标

<2> next数组的构建

1. next数组初始化
2. 处理前后缀不相同的情况
3. 处理前后缀相同的情况
4. 更新next数组的值

a = len(needle)
def getNext(a, needle):
    next = [' ' for i in range(a)]
    j, k = 0, -1
    next[0] = k
    while j < a - 1:
        if k == -1 or needle[k] == needle[j]:
            k += 1
            j += 1
            next[j] = k
        else:
            k = next[k]
    return next

2.2.2 完整题解代码

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        m, n = len(haystack), len(needle)
        if n == 0:
            return 0
        i, j = 0, 0
        next = self.getNext(n, needle)
        while (i<m and j<n):
            if j==-1 or needle[j]==haystack[i]:
                i+=1
                j+=1
            else:
                j=next[j]
        if j==n:
            return i-j
        else:
            return -1


    def getNext(self, n, needle):
        next = [' ' for i in range(n)]  # next的初始化
        j, k = 0, -1
        next[0] = k
        while (j < n-1 ) :
            if k==-1 or needle[k] == needle[j]: # 找到前缀表了
                k += 1
                j += 1
                next[j] = k
            else:
                k = next[k]
        return next

三、参考资料

四、小结

  到这里本文就结束了,感谢大家阅读,强烈推荐大家去 代码随想录 。刷题必备神器,冲冲冲!!!
  最后,觉得博主写的不错的,给博主点个赞鼓励一下吧!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦想拯救世界_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值