Day 08
KMP,一个很复杂的算法
28. 找出字符串中第一个匹配项的下标(中等)
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
解:用KMP。真想吐槽这么难想的题目居然还只是中等……
先看动图(如下):
可以看到,这个题的做法是,先一步步查找两个数组相等的地方,当查找到不等的地方的时候,模式串指针不会退回到0,而是从中间某处开始。我们会构建一个名为前缀表的数组,来提示指针不匹配时,该跳回中间某处继续查找。
这个某处要用到的东西是:当前字符串的最长公共前后缀。以aabaaf为例:
字符串前缀是指:不包含最后一个字符的所有以第一个字符开头的连续子串。即abaaf, baaf, aaf, af, f。
字符串后缀是指:不包含第一个字符的所有以最后一个字符结尾的连续子串。即aabaa, aaba, aab, aa, a。
很明显,最长公共前后缀就是前后子串中都包含、而且最长的那个,即aa。而前缀表就是一个记录当前子串的最长公共前后缀的数组。
对于aabaaf,其前缀表为a, aa, aab, aaba, aabaa, aabaaf对应的最长公共前后缀长度,即[0,1,0,1,2,0]
求前缀表next的方法见代码def strStr(注意,有些做法将前缀表统一-1,即[-1, 0, -1, 0, 1, -1],下面的动图也是,方法是一样的,我懒得-1了)
接下来用next数组做匹配。动画如下:
和求前缀表的方法类似,只不过一个指针放在文本串一个放在模式串。具体操作见代码
class Solution:
def getNext(self, s: str): # 计算next数组,即查找每一个子串的最长公共前后缀
next = [0]*len(s)
# j指向字符串最大前缀末尾,i指向字符串最大后缀末尾,也就是指向字符串末两位
j = 0
for i in range(1, len(s)):
# 如果不相等,则在前j-1个字符中(也是前缀子串)寻找最大子串判断是否相同
# 比如aaabb中i->b,j->a(i=3,j=2)时,不相等,则要找j前一个元素在next数组里的值next[j-1]=1
# j->aa的末尾a,i不动,还是不等,继续往前找
while j>0 and s[i] != s[j]:
j = next[j-1]
# 如果相等,ij同时后移一位
if s[i] == s[j]:
j += 1
next[i] = j
return next
def strStr(self, haystack: str, needle: str): # 用next数组匹配
j = 0
next = self.getNext(needle)
for i in range(len(haystack)):
# 如果不相等,则在前j-1个字符中(也是前缀子串)寻找最大子串判断是否相同
while j>0 and haystack[i] != needle[j]:
j = next[j-1]
# 如果相等,ij同时后移一位
if haystack[i] == needle[j]:
j += 1
# 移动到末尾时如果相等,则needle属于haystack的子串,此时i指向子串末尾,可求出开头位置
if j == len(needle):
return i - len(needle) + 1
# 如果没有相等末尾,则needle不属于子串,返回-1
return -1
459. 重复的子串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
解:如果s是一个子串重复多次构成,那么[s,s]中必然也能找到一个s。去掉s的首末两位防止找错,在剩下的部分如果能找到s,就说明s是一个子串重复多次构成的。
class Solution:
def getNext(self, s: str):
next = [0]*len(s)
j = 0
for i in range(1,len(s)):
while j>0 and s[i] != s[j]:
j = next[j-1]
if s[i] == s[j]:
j += 1
next[i] = j
return next
def repeatedSubstringPattern(self, s: str):
s = list(s)
if len(s) == 1:
return False
t = (s+s)[1:-1]
next = self.getNext(s)
j = 0
for i in range(len(t)):
while j > 0 and t[i] != s[j]:
j = next[j-1]
if t[i] == s[j]:
j += 1
if j == len(s):
return True
return False