力扣Day9(2.12)| 第四章 字符串 (28. 实现 strStr() 459.重复的子字符串 字符串和双指针回顾 )

题一:28. 实现 strStr()

链接

题目链接:
kmp理论视频链接:
kmp代码视频链接:
文章链接:

视频总结

关键点

编程思路

Me:
卡尔:

力扣实战

思路一:

 #朴素暴力法是基础:
 class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        a = len(haystack)
        b = len(needle)
        j = 0
        for k in range(a):
            i = k # 若不剪枝,则最外层循环变量每次加一
            while j<b and i<a and haystack[i] == needle[j]: # 两个界限缺一不可,表示的是这个while循环的退出条件,可以用例子带入理解。若不加,在等于时会出现索引越界的报错
                i +=1 	# 匹配上了就一直往后,直到不匹配或者,i或者j跑到最后了以后
                j +=1
            if j == b: # 可以通过举两个例子理解 
                return i-j
            else:
                j = 0        # 若不相等,且匹配项未结束,则匹配项从头开始,原串从第二个开始
        return -1
        
# 反思1:

思路二:

# 使用kmp算法,专门解决主串里查找是否有子串的问题
 class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        def cal(s):
            j = 0
            c = len(s)
            n = [0]*c  # 此处一开始写错了,只是定义了一个空数组,而空数组只能往末尾添加元素。不能够通过索引定位置
            for i in range(1, c):	# 双指针,左指针指向前缀的结尾,右指针指向后缀的结尾。故j到i为当前需要操作的子串,因为j初始指向0,所以i无需从零开始,若为零则0即无前缀也无后缀。j指向前缀结尾,同时也代表当前操作子串含有的最长对称子串的长度,和对称对的个数
                while j > 0 and s[i] != s[j]:   #测试用例"aabaabaafcd""aabaaf"  "sadbutsad""sad"  "sadbutsada""sada"
                    j = n[j - 1]		# j>0必不可少,若无:在起始处的位置
                if s[i] == s[j]:	# 相等时,此时j加一表示,说明前缀结尾在j,后缀结尾在i个字符长度时,存在一对相等前后缀;且长度为j+1,也就是在匹配串的开头到索引i这一段子串中,最长相等前后缀长度为j➕1.而且next数组在索引i处的值(子串最大相等子串长度),只需这一次就可以判断出来,因为这一次的j就是上一轮的next(i),正是这个j记录了之前的信息,也就是说,这里若匹配,next值一定是j+1,其他的所有情况都可以被已保存的信息(也是next数组的性质)给排除掉
                					#下一轮i增加时,操作串长度为i+1,先判断操作串的最长子串是不是对称,那么此时j = i-1,因为上一轮已经记录了最长子串的最后一个元素之前都是相同的,所以此时如果ij对应元素相同,那么j在上一轮的基础上继续加一即可,无需再判断操作串的更小的子串!
                					#若j在开头了,还不相等,说明这一轮长度为i+1的子串没有长度为一的最短相同前后缀,也就意味着操作串的相同前后缀的个数为零,这个j可以赋值给next数组对应的i的位置,即长度为i+1时无相同前后缀。
                    j += 1
                n[i] = j	# 而j>0且不等。
            return n

        next = cal(needle)
        i = 0
        j=0
        a=len(haystack)
        b=len(needle)
        while i <a: 	# 双指针指向两个字符串开头
            if haystack[i] == needle[j]: 	# 相等时一直往后,直到不等、或者发现j == b,也就是匹配串全部都相等。
                i +=1 
                j +=1
            else:		
                if j:		#如果不等 且j大于零 此时利用next指针的性质,在j处不等,则说明j之前都等,那么j之前的串的部分的next值的索引为j-1,只需从他最长对称前缀的后面开始匹配即可,因为两个字符串是在i处才不等的,也就是在i之前都是相等的,那么若是之前的这一段子字符串有对称的部分,比如是二,那么i之前有两个字符一定和匹配串的首两个字符串同,那么只需要对比第三个元素是否等于i。等于就继续往后,若不等,且j大于零,则说明j之前都等,虽然可能之前匹配的动作被剪枝了,一直跳到j=0,还不等则说明一开始这里就不等,于是i需要加一操作,用匹配串的开头和i串互怼。
                    j = next[j-1]
                else:		# 若不等且j等于零,那么匹配串后面的元素也没必要比了,直接比i串后面的内容
                    i += 1
            if j ==b:
                return i-j
        return -1

文档总结

1. next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)
2. 如果 s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。
	##我的理解:因为当s[i] 与 s[j+1] 相同时,(j+1是为了不出现j-1),只需这一个步骤就可以判断next[i]的值,这代表着J也就是上一轮的next[i]储存的信息发挥着作用。

题二: 459.重复的子字符串

链接

题目链接:
视频链接:
文章链接:

视频总结

关键点

编程思路

Me:
卡尔:
  1. 把两个相同的主串合并
  2. 去掉开头结尾
  3. 插主串是否在长串中,在则说明是重复的子串(但是这里目前我只能通过画图来理解,还无法证明,或许假设n个是可以的)

力扣实战

思路一:

 class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        ss = s+s
        ss = ss[1:]
        ss = ss[:-1]  #索引里-1是最后一个,截取里-1是倒数第二个
        def cal(s):
            l = len(s)
            j = 0
            n=[0]*l
            for i in range(1,l):
                while j>0 and s[i] !=s[j]:
                    j = n[j-1]
                if s[i]==s[j]:
                    j+=1
                n[i]=j
            return n
        n = cal(s)
        l1 = len(s)
        l2= len(ss)
        i = j = 0
        while i<l2:
            while j < l1 and i< l2 and ss[i]==s[j]: # i和j都需要设置范围,否则会越出数组的界限,这里用if 语句也可以,使用if语句就不需要前面两个and了,把下面为true的情况先考虑即可
                i += 1
                j += 1
            if j == l1:
                return True
            else:
                if j:
                    j = n[j-1]
                else:
                    i += 1
        return False
        
# 反思1:

思路二:

# 通过性质:数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。这里思路二通过画图举例确实是有这个性质,非常巧妙,二刷可以深究。
 class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        def cal(s):
            l = len(s)
            j = 0
            n=[0]*l
            for i in range(1,l):
                while j>0 and s[i] !=s[j]:
                    j = n[j-1]
                if s[i]==s[j]:
                    j+=1
                n[i]=j
            return n
        n = cal(s)
        if n[-1] != 0 and len(n)%(len(n)-n[-1])==0:
            return True
        else:
            return False

文档总结

1. 本题文档值得反复学习,价值较高

题三:字符串回顾

文章链接:

题四:双指针回顾

文章链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值