LeetCode-53 实现strStr()

LeetCode-53 实现strStr()

1. 题目

实现 strStr() 函数。
    给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回  -1 。
说明:
    当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

2. 朴素匹配

暴力匹配算法,从头开始一个字符一个字符匹配,时间复杂度为 $ O(m*n) $

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

3. KMP算法

  • next数组构建
    KMP算法的时间复杂度为 \(O(m+n)\)
    KMP算法的核心在于next数组的构建,next数组中,next[i]代表的是最长的公共前缀后缀长,例如对如字符串"ababc",
    首先前缀是指不包含最后一个值的字符串,后缀指不含第一个值的字符串
    i=0时,a最长的前后缀为'',next[0]=0
    i=1时,ab最长的前后缀为'',next[1]=0
    i=2时,aba的最长前后缀为'a',next[2]=1
    i=3时,abab的最长前后缀为'ab',next[2]=2
    i=3时,ababc的最长前后缀为'',next[2]=0
    要推断字符串的最长前后缀长度的复杂度是 $ O(n^2) $ ,但在构建的过程中是有规律的
    对于ababc:
    初始化:next=[0]*len
    第一个字符的next值为0
    接着遍历后面的字符
    用k来代表当前最长前后缀的起始位置
    此时i=1,k=0
    对于字符b,此时k=0,表示当前最大前后缀的起始位置为0,判断a!=b,所以next[1]=0,并将i后移
    此时i=2,k=0
    对于字符a,因为a(i=2)a(k=0),代表有相同的前后缀,将k后移,且next[2]=1,并将i,k后移
    此时i=3,k=1
    对于字符b,因为a(i=3)
    a(k=1),代表有相同的前后缀,将k后移,且next[3]=2,并将i,k后移
    此时i=4,k=2
    对于字符c,因为a(i=4)!=a(k=2),没有相同的前后缀,此时k退回next[k-1],next[k-1]表示k前面一个位置的最长前后缀的长,k=next[k-1]=next[1]=0,当k=0 or s[i]s[k]时,循环结束,如果此时k0 and s[i]!=s[k],则该位置最长前后缀为0,否则为next[k]
    QQ图片20210423110735.png
  • 匹配过程
    QQ图片20210423103643.png
    在第三个字符匹配错误时,KMP算法比朴素算法的优点就体现出来了,朴素匹配算法会直接进入下一个字符,然后从头开始
    QQ图片20210423103934.png
    KMP算法会根据next数组,直接选择有公共前后缀的部分,跳过已经匹配过的部分,只经过一次遍历即可匹配
    QQ图片20210423104230.png
  • 代码
    class Solution:
        def strStr(self, haystack: str, needle: str) -> int:
            next_=self.get_next_(needle)
            i=0
            j=0
            res=False
            while i<len(haystack):
                while haystack[i]!=needle[j] and j>0:
                    j=next_[j-1]
                if haystack[i]==needle[j]:
                    j+=1
                if j==len(needle):
                    return True
                i+=1
            return False
            pass
        def get_next_(self,s: str):
            next_=[0]*len(s)
            i=1
            k=0
            while i<len(s):
                while s[i]!=s[k] and k!=0:
                    k=next_[k-1]
                if s[i]==s[k]:
                    k+=1
                next_[i]=k
                i+=1
            return next_
    
posted @ 2021-10-16 10:30  流光之中  阅读(27)  评论(0编辑  收藏  举报