• 字符串 | 字符串匹配 | KMP算法 | leecode刷题笔记


    跟随carl代码随想录刷题
    语言:python


    KMP算法

    KMP命名来源

    • 命名来源:三位作者的名字首字母

    KMP主要用于解决字符串匹配问题

    当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,利用这些信息,从而避免从头再去匹配。
    💁举例说明如何记录已匹配的内容

    • 文本串(haystacka a b a a b a a f
      模式串(needlea a b a a f
      目的:要在文本串中查找是否出现过这个模式串
      请添加图片描述

    • 复杂度:O(n+m),其中n是文本串长度,m是模式串长度

      • 暴力解法的复杂度是O(n*m)
    • 前缀表

      • 功能:用于回退。记录模式串与主串不匹配时,模式串应该从哪里重新开始匹配
      • 内容:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
      • 目标:找最长相等前后缀长度
        • 前缀:不包含尾字母所有以首字母开头的字符串
          • aaaaabaabaaabaa
        • 后缀:不包含首字母所有以尾字母结尾的字符串
          • fafaafbaafabaaf
        • 当匹配失败的位置是后缀字串的后面,那么就找到与其相同的前缀的后面位置,然后重新开始匹配
      • 计算前缀表
        • 模式串a a b a a f的前缀表如下:
    字符串最长相等前后缀
    a0
    aa1
    aab0
    aaba1
    aabaa2
    aabaaf0

    next数组

    有三种:

    • 前缀表对应位置上的数值减1(如:-1 0 -1 0 1 -1
    • 前缀表数值统一右移一位,首位补-1(如:-1 0 1 0 1 2
    • 前缀表本身(如:0 1 0 1 2 0

    三种next数组都可以用,只不过在具体代码中对应的回退条件设置有所区别。建议熟悉哪种就用哪种,坚持循环不变量原则!
    三种方法分别对应的回退条件为:

    • 回退到索引==不匹配位置的前一位的“最长相等前后缀长度” + 1
    • 回退到索引==不匹配位置的“最长相等前后缀长度”
    • 回退到索引==不匹配位置的前一位的“最长相等前后缀长度”
      请添加图片描述

    构造next数组

    构建next数组,函数参数为指向next数组的指针,和一个字符串。 代码如下:
    构造next数组其实就是计算模式串s的前缀表的过程。 主要有如下三步:

    初始化
    定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。

    处理前后缀不相同的情况
    处理前后缀相同的情况

    28. 简单实现 strStr()

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

    暴力解法

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

    KMP算法

    代码

    class Solution:
        def strStr(self, haystack: str, needle: str) -> int:
            n = len(needle)
            m = len(haystack)
    
            if n==0:
                return 0  # 这步很重要
            
            i = j = 0
            next = self.getnext(n, needle)  # 构造next数组
            while j<n and i<m:
                if j==-1 or needle[j]==haystack[i]:
                    j += 1
                    i += 1
                else:
                    j = next[j]  # 如果不相等就回退,调用next数组,用于告诉我们应该回退的位置
            if j==n:
                return i-j  # 返回索引
            else:
                return -1  # 不存在
            
        def getnext(self, n, needle):  # 记录最长相等前后缀的长度
            next = ['' for i in range(n)]  # 初始化数组
            j, i = 0, -1
            next[0] = i
            while(j<n-1):
                if i==-1 or needle[i]==needle[j]:
                    i += 1
                    j += 1
                    next[j] = i  
                else:
                    i = next[i]  # 当前不相等的位置是j时,应该回退到i
            return next
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    画图理解

    请添加图片描述
    太绕了!!!
    请添加图片描述

    459. 简单重复的子字符串

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

    题目分析

    完整代码

    class Solution:
        def repeatedSubstringPattern(self, s: str) -> bool:
            if len(s) == 0:
                return False
            nxt = [0] * len(s)
            self.getNext(nxt, s)
            if nxt[-1] != 0 and len(s) % (len(s) - nxt[-1]) == 0:
                return True
            return False
        
        def getNext(self, nxt, s):
            nxt[0] = 0
            j = 0
            for i in range(1, len(s)):
                while j > 0 and s[i] != s[j]:
                    j = nxt[j - 1]
                if s[i] == s[j]:
                    j += 1
                nxt[i] = j
            return nxt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    待学……💫

  • 相关阅读:
    C++中struct与class区别,C与C++中struct区别
    Eigen::svd和 np.linalg.svd的不同之处
    es6 面试 proxy,map与WeakMap, set 与 weakSet,symbol,class 继承
    无向图的双连通分量算法详解 + 模板题
    Android移动安全攻防实战 第二章
    video caption with frame selection【论文阅读】
    Interval Envision图像库,非常强大的图像功能
    嵌入式单片机学习入门到大牛
    低代码:降低技术能力要求,提升软件开发效率
    开源许可证概述:GNU, BSD, Apache, MPL, 和 MIT
  • 原文地址:https://blog.csdn.net/qq_44250700/article/details/126227302