题目描述:Leetcode 28. Implement strStr()
之前在 Leetcode 上 AC 的 O(MN) 版本:Q28 Implement strStr()
KMP 算法是经典的求解子串(模式串)出现在主串中位置的算法,也是数据结构当时学习的一个知识点。它因为在匹配过程中,主串下标不后退,而可以使时间复杂度从 O(MN) 降为 O(M+N) 。之前学过忘了,现在在此做一个总结。
KMP 算法的关键:求出子串(模式串)的 next 数组。
举例:
子串 pattern 下标 0 1 2 3 4 5 6 7 8 9 10 11 12
子串 pattern a b c d a z a a a b c a b
其对应的 next 数组为 0 0 0 0 1 0 1 1 1 2 3 1 2
举例理解 next :
第 2 个 ‘b’ 对应的 next9 = 2 ,其意义为从 ‘b’ 往前数,找到一个和从首字符开始相等的最长子串(‘ab’)。而第 2 个 ‘c’ 对应 next10 = 3,是因为从 ‘c’ 往前数,可以找到的与首字符开始数的相等的最长子串为 ‘abc’。其他同理。
如何求解 next 数组?
如何利用 next 数组进行匹配?
简单来说,遍历主串,一个一个匹配,如果相等,主串和子串都向后移动一位,记录匹配的长度加 1;如果匹配失败,则主串的下标不变,子串的下标变为失配字符的前一个 next 数组的值,即 j = next[j-1]
,同时,匹配的长度要更新为 j 而不是 0,因为前 j 个字符是匹配的。如果失配的字符下标 j = 0(下面例子的第四次匹配),说明从头都没有找到最长子串,这时主串的下标要加1,从下一个字符开始找,不然会陷入死循环。
可以结合下面这个例子理解上面这段话的匹配过程:
子串的下标 0 1 2 3 4 5 6 7 8 9
子串 pattern a b c d a a b c a b
子串的 next 数组 0 0 0 0 1 1 2 3 1 2
class Solution:
# KMP 算法
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
# 边界情况处理
if len(needle) == 0:
return 0
if len(haystack) < len(needle):
return -1
# 产生 next 数组
next = self.nextArr(needle)
# print(next)
i = j = 0
length = 0
while i < len(haystack):
if haystack[i] != needle[j]:
if j == 0: # 到头还是无法匹配
i += 1
else:
j = next[j-1]
length = j # 已经比较过的长度
else:
length += 1
i += 1; j += 1
if length == len(needle):
return i - length
return -1 # 未找到
# 求 next 数组
def nextArr(self, needle):
lens = len(needle)
next = [0] * lens
for i in range(1, lens):
preNextVal = next[i-1]
while preNextVal > 0 and needle[i] != needle[preNextVal]:
preNextVal = next[preNextVal-1]
if needle[i] == needle[preNextVal]:
preNextVal += 1
next[i] = preNextVal
return next
# next 数组验证
print(Solution().nextArr('abcdazaaabcab')) # [0, 0, 0, 0, 1, 0, 1, 1, 1, 2, 3, 1, 2]
print(Solution().nextArr('abababzabababa')) # [0,0,1,2,3,4,0,1,2,3,4,5,6,5]
# 子串匹配 Test1
haystack = 'ababcdaabccabcdaabcab'
needle = 'abcdaabcab' # 对应的 next = [0, 0, 0, 0, 1, 1, 2, 3, 1, 2]
print(Solution().strStr(haystack, needle)) # 12
# 子串匹配 Test2
haystack = "ississip"
needle = "issip" # 对应的 next = [0,0,0,1,0]
print(Solution().strStr(haystack, needle)) # 3