@[TOC](训练营day9|KMP| 28. 实现 strStr() 、459.重复的子字符串)
KMP
理论基础
- KMP思想:当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,利用这些信息避免从头再做匹配。
- 理解前后缀
1)前缀:不包含最后一个字符的所有以第一个字符开头的连续子串。
2)后缀:不包含第一个字符的所有以最后一个字符结尾的连续子串。 - 前缀表:
1)用来回退,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
2)记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
28. 实现 strStr()
要点
- 理解了前缀表和最长相等前后缀的原理和作用,但对于代码的回退没能特别清晰,即对于前后缀不相同时的回退过程不够直观,二刷再看看
代码
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
a = len(needle)
n = len(haystack)
if a == 0:
return 0
next_list = self.getnext(a, needle)
j = -1
for i in range(n):
while j >= 0 and needle[j + 1] != haystack[i]:
j = next_list[j]
if needle[j + 1] == haystack[i]:
j += 1
if j == a-1:
return i - j
return -1
def getnext(self, a, needle):
next_list = ['' for i in range(a)]
j = -1
next_list[0] = j
for i in range(1, a):
while j >= 0 and needle[i] != needle[j + 1]:
j = next_list[j]
if needle[i] == needle[j + 1]:
j += 1
next_list[i] = j
return next_list
459.重复的子字符串
要点
1.采用移动匹配,但大部分题解都只证明了如果s满足重复子串性质,那么s+s去头去尾肯定还会找到一个s,即充分性,但没有证明必要性,即s+s去头去尾能找到一个s就代表说了满足重复子串性质吗。官方题解中关于必要性有一个证明过程。移动匹配最后需要用到库函数,也是O(n+m)的时间复杂度
2.采用kmp算法。
代码
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
#return (s + s).find(s, 1) != len(s) 移动匹配,用了find函数,该函数也可能用的kmp算法,比下面具体的kmp算法要快
if len(s) == 0:
return False
next_list = self.getnext(s)
if next_list[-1] != -1 and len(s) % (len(s) - next_list[-1] - 1) == 0:
return True
return False
def getnext(self, s):
next_list = [''] * len(s)
j = -1
next_list[0] = j
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j + 1]:
j = next_list[j]
if s[i] == s[j + 1]:
j += 1
next_list[i] = j
return next_list