一、背景
给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题。
Knuth-Morris-Pratt 算法(简称 KMP,网上有人戏称“看毛片”,我的输入法打出来的首位竟然是“烤馍片”)是解决这一问题的常用算法之一,这个算法是由高德纳(Donald Ervin Knuth)和沃恩·普拉特在 1974 年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终三人于 1977 年联合发表。
二、暴力匹配(朴素字符串匹配算法)
看到这样的问题,对我这个菜鸟来说,第一反应当然就是暴力匹配,代码如下:
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
start = 0
if needle == '':
return (start)
while True:
if start + len(needle) > len(haystack):
return (-1)
else:
if haystack[start:start + len(needle)] == needle:
return (start)
else:
start += 1
暴力匹配的时间复杂度为 O(nm),其中n为 S 的长度,m为 P 的长度。很明显,肯定还有更优的匹配方法
三、KMP字符串匹配算法
这里,我在网上搜了一下,看了几篇,大部分一开始看下来是真的有点懵逼,其中有一篇,至少我看下来是看懂了,虽然把这篇文章收藏了,但是担心万一哪一天要收费或者文章没了(之前还真发生过),所以自己还是截图贴在博客里,随时可以看看回顾(当然在这篇文章最后,我会附上原文链接)
这儿我就放上自己的Python代码:
#未优化的next数组
def GetNext(P, NEXT):
p_len = len(P)
i = 0
j = -1
next.append(-1)
while (i < p_len-1):
#print (P[i] + 'and' + P[j])
if (j == -1 or P[i] == P[j]):
i += 1
j += 1
next.append(j)
else:
j = next[j]
P = "ABCDABCE"
next = []
GetNext(P,next)
print (next)
个人还是比较赞同网上很多作者的说法,即next数组的确是KMP的核心。j=next[i]这个更是让我直接跪了,核心中的灵魂;用网上一篇内容说的:初学者能一眼看明白j=next[i]这一步的,请允许我尊称一声“大神”
四、KMP优化
其实简单点说,就是在"i=4"的时候,要考虑一下这个第二个“A”后面的字符和第一个“A”后面的字符是否相等,如果相等,那next[5]的值为“0”,如果不相等,则为“1”;代码如下:
#优化后的next数组
def GetNext(P, NEXT):
p_len = len(P)
i = 0
j = -1
next.append(-1)
while (i < p_len-1):
#print (P[i] + 'and' + P[j])
if (j == -1 or P[i] == P[j]):
i += 1
j += 1
if P[i] != P[j]:
next.append(j)
else:
next.append(next[j])
else:
j = next[j]
#print (j)
P = "ABCDABCE"
next = []
GetNext(P,next)
print (next)
五、完整代码
#! /usr/bin/python
# -*- coding:utf-8 -*-
'''
#未优化next数组
def GetNext(P, NEXT):
p_len = len(P)
i = 0
j = -1
next.append(-1)
while (i < p_len-1):
#print (P[i] + 'and' + P[j])
if (j == -1 or P[i] == P[j]):
i += 1
j += 1
next.append(j)
else:
j = next[j]
P = "ABCDABCE"
next = []
GetNext(P,next)
print (next)
'''
#优化后next数组
def GetNext(P, NEXT):
p_len = len(P)
i = 0
j = -1
next.append(-1)
while (i < p_len-1):
#print (P[i] + 'and' + P[j])
if (j == -1 or P[i] == P[j]):
i += 1
j += 1
if P[i] != P[j]:
next.append(j)
else:
next.append(next[j])
else:
j = next[j]
#print (j)
P = "ABCDABCE"
next = []
GetNext(P,next)
print (next)
def KMP(S, P):
i = 0
j = 0
s_len = len(S)
p_len = len(P)
while (i < s_len and j < p_len):
if (j == -1 or S[i] == P[j]):
i += 1
j += 1
else:
j = next[j]
if j == p_len:
#print (i-j)
return (i-j)
return (-1)
S = "ABDEABCDABCE"
KMP(S,P)
参考文章链接:KMP算法