Leetcode刷题记录(代码随想录day08)

Day 08

KMP,一个很复杂的算法



28. 找出字符串中第一个匹配项的下标(中等)

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

:用KMP。真想吐槽这么难想的题目居然还只是中等……
先看动图(如下):
在这里插入图片描述
可以看到,这个题的做法是,先一步步查找两个数组相等的地方,当查找到不等的地方的时候,模式串指针不会退回到0,而是从中间某处开始。我们会构建一个名为前缀表的数组,来提示指针不匹配时,该跳回中间某处继续查找。

这个某处要用到的东西是:当前字符串的最长公共前后缀。以aabaaf为例:
字符串前缀是指:不包含最后一个字符的所有以第一个字符开头的连续子串。即abaaf, baaf, aaf, af, f。
字符串后缀是指:不包含第一个字符的所有以最后一个字符结尾的连续子串。即aabaa, aaba, aab, aa, a。
很明显,最长公共前后缀就是前后子串中都包含、而且最长的那个,即aa。而前缀表就是一个记录当前子串的最长公共前后缀的数组。
对于aabaaf,其前缀表为a, aa, aab, aaba, aabaa, aabaaf对应的最长公共前后缀长度,即[0,1,0,1,2,0]
求前缀表next的方法见代码def strStr(注意,有些做法将前缀表统一-1,即[-1, 0, -1, 0, 1, -1],下面的动图也是,方法是一样的,我懒得-1了)
在这里插入图片描述

接下来用next数组做匹配。动画如下:
在这里插入图片描述
和求前缀表的方法类似,只不过一个指针放在文本串一个放在模式串。具体操作见代码

class Solution:
    def getNext(self, s: str):  # 计算next数组,即查找每一个子串的最长公共前后缀
        next = [0]*len(s)
        # j指向字符串最大前缀末尾,i指向字符串最大后缀末尾,也就是指向字符串末两位
        j = 0
        for i in range(1, len(s)):
            # 如果不相等,则在前j-1个字符中(也是前缀子串)寻找最大子串判断是否相同
            # 比如aaabb中i->b,j->a(i=3,j=2)时,不相等,则要找j前一个元素在next数组里的值next[j-1]=1
            # j->aa的末尾a,i不动,还是不等,继续往前找
            while j>0 and s[i] != s[j]:
                j = next[j-1]
            # 如果相等,ij同时后移一位
            if s[i] == s[j]:
                j += 1
            next[i] = j
        return next

    def strStr(self, haystack: str, needle: str):  # 用next数组匹配
        j = 0
        next = self.getNext(needle)
        for i in range(len(haystack)):
            # 如果不相等,则在前j-1个字符中(也是前缀子串)寻找最大子串判断是否相同
            while j>0 and haystack[i] != needle[j]:
                j = next[j-1]
            # 如果相等,ij同时后移一位
            if haystack[i] == needle[j]:
                j += 1
            # 移动到末尾时如果相等,则needle属于haystack的子串,此时i指向子串末尾,可求出开头位置
            if j == len(needle):
                return i - len(needle) + 1
        # 如果没有相等末尾,则needle不属于子串,返回-1
        return -1

459. 重复的子串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

:如果s是一个子串重复多次构成,那么[s,s]中必然也能找到一个s。去掉s的首末两位防止找错,在剩下的部分如果能找到s,就说明s是一个子串重复多次构成的。

class Solution:
        def getNext(self, s: str):
            next = [0]*len(s)
            j = 0
            for i in range(1,len(s)):
                while j>0 and s[i] != s[j]:
                    j = next[j-1]
                if s[i] == s[j]:
                    j += 1
                next[i] = j
            return next
    
        def repeatedSubstringPattern(self, s: str):
            s = list(s)
            if len(s) == 1:
                return False
            t = (s+s)[1:-1]
            next = self.getNext(s)
            j = 0
            for i in range(len(t)):
                while j > 0 and t[i] != s[j]:
                    j = next[j-1]
                if t[i] == s[j]:
                    j += 1
                if j == len(s):
                    return True
            return False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值