第一题
28. Find the Index of the First Occurrence in a String
Given two strings
needle
andhaystack
, return the index of the first occurrence ofneedle
inhaystack
, or-1
ifneedle
is not part ofhaystack
.
首先想到的是用双指针进行匹配
### ***WRONG Answer***
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
pHay, pNee = 0, 0
result = 0
while pHay <= len(haystack)-1:
for i in range(len(haystack)):
if haystack[i] == needle[pNee]:
result = i
while pNee <= len(needle)-1:
for j in range(len(needle)):
if haystack[i+j] == needle[j]:
pNee += 1
else:
return -1
return result
else:
return -1
pHay += 1
但是套了两层while和for,不仅代码可读性非常差,还有测试用例通不过。看来这不是正确的办法。
开始今天的学习:KMP算法。这道题是一道KMP算法的经典题。
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
什么是KMP
说到KMP,先说一下KMP这个名字是怎么来的,为什么叫做KMP呢。
因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP
什么是前缀表
写过KMP的同学,一定都写过next数组,那么这个next数组究竟是个啥呢?
next数组就是一个前缀表(prefix table)。
前缀表有什么作用呢?
前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。前缀表的任务是当前位置匹配失败,找到之前已经匹配上的位置,再重新匹配,此也意味着在某个字符失配时,前缀表会告诉你下一步匹配中,模式串应该跳到哪个位置。那前缀表该如何发挥作用呢?首先要计算最长公共前后缀。
如何计算最长相等前后缀
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
比如aabaaf
前缀有a、aa、aab、aaba、aabaa
后缀有f、af、aaf、baaf、abaaf
拿前缀来说,a的最长相等前后缀是0,aa是1,aab是0,aaba是1,aabaa是2
最长的就是2,因此在使用KMP匹配时,可以从下标是2的位置重新开始匹配,不用重头开始
使用KMP算法的解法如下
class Solution:
def getNext(self, next, s):
j = -1
next[0] = j
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j+1]:
j = next[j]
if s[i] == s[j+1]:
j += 1
next[i] = j
def strStr(self, haystack: str, needle: str) -> int:
if not needle:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = -1
for i in range(len(haystack)):
while j >= 0 and haystack[i] != needle[j+1]:
j = next[j]
if haystack[i] == needle[j+1]:
j += 1
if j == len(needle) - 1:
return i - len(needle) + 1
return -1
第二题
459. Repeated Substring Pattern
Given a string
s
, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together.
又是一道可以用KMP算法解的题,实际上next 数组记录的就是最长相同前后缀,求出next数组,这道题也就完成了大半。
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] != -1 and len(s) % (len(s) - (nxt[-1] + 1)) == 0:
return True
return False
def getNext(self, nxt, s):
nxt[0] = -1
j = -1
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j+1]:
j = nxt[j]
if s[i] == s[j+1]:
j += 1
nxt[i] = j
return nxt