今日任务:
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
思路--常规法:
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算法,再一点点逐个击破