28. 实现strStr()
- KMP算法,原来是这个,之前上数据结构课的时候应该是学过的,但是早就忘了,现在虽然有点印象,但细节早就不记得了。跟着视频捋了一遍还是很懵。
- 印象中的next表计算是右移的那个表,也就是不需要判断j>0那一步,但是现在感觉直接用原始前缀表比较好理解。
- 需要注意回退的时候用while,且需要判断j>0。
- 时间复杂度O(m+n),空间复杂度O(m)–>next表
- 计算next表也可以看成是一个前后缀的匹配,因此回退的时候也使用next[j-1]。但是具体逻辑还没有很清楚。
- 放几个b站视频下面的解答:[1, i]是后缀,是因为后缀不包括首字母,i是后缀末尾。[0, j]是前缀,j是前缀末尾。因此就是一个前缀和后缀的匹配。同时j也是已经匹配的字符串长度。每次如果想等的话,i和j都会后移一位。
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# KMP算法
def get_next(needle):
next = [0] * len(needle)
j = 0
for i in range(1, len(needle)):
while(j > 0 and needle[i] != needle[j]):
j = next[j-1]
if needle[i] == needle[j]:
j+= 1
next[i] = j
return next
next = get_next(needle)
j = 0
for i in range(len(haystack)):
while j > 0 and haystack[i] != needle[j]:
j = next[j-1]
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return i - len(needle) + 1
return -1
class Solution(object):
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
m, n = len(haystack), len(needle)
for i in range(m):
if haystack[i:i+n] == needle: # 不怕越界的
return i
return -1
一句话python
return haystack.find(needle)
459. 重复的子字符串
- 并没有搞懂为什么s+s中出现另一个s是循环的必要充分条件。必要条件明白,充分条件是为什么。大概写了一下,可以等价为如果s=a+b=b+a,那么一定是循环,但是怎么严谨证明呢?反证法有没有可能。
- 感觉下一次刷肯定会忘掉啊啊啊
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
ss = s[1:] + s[:-1] # 掐头去尾
# 用KMP在ss中匹配s
KMP_next = self.get_next(s)
j = 0
for i in range(len(ss)):
while j > 0 and ss[i] != s[j]:
j = KMP_next[j-1]
if ss[i] == s[j]:
j += 1
if j == len(s):
return True
return False
def get_next(self, s):
j = 0
KMP_next = [0] * len(s)
for i in range(1, len(s)):
while(j > 0 and s[i] != s[j]):
j = KMP_next[j-1]
if s[i] == s[j]:
j += 1
KMP_next[i] = j
return KMP_next
总结
- 反转字符串,本身使用双指针来实现本地反转:指针可以考虑从前往后还是从后往前
- 字符串替代
- KMP:字符串匹配/重复子字符串
- 双指针的一些使用场景:
- 数组移除元素,一个指针遍历一个指针修改,来实现本地修改的时间复杂度O(1),而不是移除一个后面的都移动的O(n)
- 字符串:反转字符串、字符串填充替代、字符串删除空格
- 链表:反转链表、快慢指针(判断环,很难想啊,只能熟能生巧了)、找环的入口(相遇后再把一个指针放回开头,然后一个速度移动)
- 两数之和(哈希)、三数之和/四数之和(排序后+双指针)