算法打卡Day08

今日任务:

1)28. 实现 strStr()

2)459.重复的子字符串 【理解,后续补充】

实现strStr()

题目链接:28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

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

示例 1:
输入:
haystack = "hello"
needle = "ll"
输出: 2

示例 2:
输入:
haystack = "aaaaa"
needle = "bba"
输出: -1

说明: 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

文章讲解:代码随想录 (programmercarl.com)

视频讲解:

帮你把KMP算法学个通透!(理论篇)哔哩哔哩bilibili

帮你把KMP算法学个通透!(求next数组代码篇)哔哩哔哩bilibili

KMP字符串匹配算法1_哔哩哔哩_bilibili

思路--常规法:

1)定义连两个指针,h指针指向文本串,n指针指向模式串

2)遍历模式串needle,与文本串haystack匹配,全匹配则成功;

     当出现不匹配时,如,指针 n 指向 f 时,则出现不匹配,则指针h向后移动,模式串也向后移动,指针n回到首位

3)重复2)过程直到匹配,或者模式串越界

这种方法很明显时间复杂度为O(len(haystack)*len(needle))

思路--KMP算法:

1)同样的定义两个指针,h指针指向文本串,n指针指向模式串

2)h,n同时移动比较,全匹配则成功

     当指针n 指向 f 时则出现了不匹配

        此时,不将指针返回首位,而是返回b继续比较(为什么返回b,这里暂时不纠结,后面详细解释)

3)成功则匹配,不成功重复2)过程直到匹配,或者模式串越界

KMP算法为什么跳向b:

这里用到了一个思想,最长相等前后缀

当指针n指向f时出现了不匹配

我们需要找到f位置前的字串aabaa的最长相等前后缀长度

可以看到字串aabaa最长相等的前后缀长度为2

所以指针n跳到最长相等前后缀中前缀的后一位,也就是b的位置

代码中不可能去移动数组,那么如何用数值确定的b的位置?其实可以发现b就是最长相等前后缀中前缀的后一位

在字串aabaa中,最长相等前后缀的长度为2,而b的下标就是2

规律已经很明显了,当指针n移动到某一位出现不匹配时,计算出指针前一位字串的最大前后缀长度maxlen,其指针n应该跳转到与长度相等下标的位置-->n = maxlen

其实也就是最长相等前后缀的长度

因为不确定模式串needle的指针移动到什么位置出现不匹配,所以最好定义一个maxlen列表存储该位置字串(注意是该位置字串,不是该位置出现不匹配)的最长相等前后缀的长度

(有很多解释把这个maxlen列表视作prefix列表,也有的叫next列表)

那么如何求得这个maxlen列表,此时我们只关注模式串

第一个maxle[0]=0

第二个maxle[1]=0

第三个maxle[2]=1

第四个maxle[3]=2

那么每一个maxlen之间有美没有关系呢?

有的,下一个maxlen可以通过上一个maxlen求得

例如:

已知maxlen[2]=1,即cur_maxlen=1

当新增一个b时,我只需要比较b与上一个最长前缀的后一位(也就是needle[1])比较,

如果相等,则cur_maxlen+ 1

如果不等呢?

求maxlen[4],此时cur_maxlen=maxlen[3]=2

判断needle[4] != needle[2],则cur_maxlen重新赋值为needle[2]前一位的maxlen值,也就是cur_maxlen = maxlen[needle[2]-1]=0,

needle[4]与needle[0]比较,还是不相等则maxlen[4]=0,如果相等则返回maxlen[4]=cur_maxlen + 1=1

class Solution:
    # todo 1.求最长相等前后缀表
    def maxPrefix(self, p: str):
        # 列表maxlen中存的是当前位置最长相等前后缀的长度
        maxlen = [0] * len(p)

        # 初始化,字符串第一个字符没有前后缀,所以maxlen直接为0
        maxlen[0] = 0

        # i后缀末尾,j前缀末尾,这里我们没有用j,而j与maxlen[i]的关系为:j = maxlen[i] - 1
        for i in range(1,len(p)):
            cur_maxlen = maxlen[i - 1]  # 现在要求maxlen[i],而此刻已知maxlen[i-1],所以当前已知最长前后缀cur_maxlen应该是前一个位置的,也就是maxlen[i-1]。【这个maxlen】
            # print(f'第{i}次循环,求maxlen[{i}],cur_maxlen=maxlen[{i - 1}]={maxlen[i - 1]},',end='')


            while cur_maxlen > 0 and p[i] != p[cur_maxlen]:
                cur_maxlen = maxlen[cur_maxlen - 1]

            # 从这个循环出来,要么cur_maxlen=0,要么p[i] = p[cur_maxlen]
            if p[i] == p[cur_maxlen]:
                cur_maxlen += 1

            maxlen[i] = cur_maxlen
            # print(f'求得maxlen[i]为{maxlen[i]}')

            i += 1

        return maxlen

    def strStr(self, haystack: str, needle: str) -> int:
        if len(needle) == 0:
            return 0

        maxlen = self.maxPrefix(needle)
        n = 0
        for h in range(len(haystack)):
            # 遇到不匹配的重新调整指针
            while n > 0 and haystack[h] != needle[n]:
                n = maxlen[n-1]  # 从这里出来的n,也就是maxlen[n-1]的值最大为len(needle)-1

            # 调整完指针后开始逐一比较
            if haystack[h] == needle[n]:
                n += 1

            # 当 n 超出范围,则跳出
            if n > len(needle) - 1:
                return h - n + 1

        return -1

感想:

这题很复杂,要先弄明白kmp算法,再一点点逐个击破

  • 47
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值