KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)
KMP算法是由Knuth、Morris和Pratt同时设计实现的,因此简称KMP算法。字符串匹配是计算机频繁进行的非常频繁的算法。其中比较经典的算法就是BF算法和KMP算法,网上对于这两种算法讲解的也比较多,BF算法比较好理解,所以在这里就不再赘述。我们来简单的描述一下KMP算法以及代码实现(基于Python)。
这两种算法都是用模式串(p)去匹配主串(s)。在BF算法,每次匹配失败的时候,都要回溯到主串下一个字符继续和模式串开头比较,在模式串比较长的时候,这种算法的效率就比较低。
KMP算法的改进在于引进了next[]数组,在匹配过程中产生“失配”时,主串不用回溯,而模式串则退回next[]数组所指示的位置。
在数据结构教材中,next[]数组的公式如下:
这个公式看起来就让人头大,直接劝退了很多人。在这里我用我的话描述一遍:
next[j]=t 代表在模式串第j个字符在与主串匹配失败时,模式串用过退回到第t个字符。
对于模式串P:abaabcac
next[1]=0 第1个字符和主串匹配失败时,因为前面根本没有元素,所以不用退,规定任何情况下,next[1]=0。
next[2]=1 第2个字符和主串匹配失败时,退回第1个字符。
next[6]=3 第6个字符和主串匹配失败时,退回到第3个字符。
拿next[6]=3举例:
当第六个字符失配时,我们把前面的字符看成一个数组。
temp[5]="abaab"
这个数组中我们规定下标从1开始,明显的t1t2=t4t5。公式中k-1=2,所以k=3;
即next[6]=3。
下面是代码实现:
def Get_next(p,next): #求模式串p的next数组的值
nums=len(p) #模式串中的元素个数
for m in range(1,nums): #从第1号元素开始算next数组值,第0号元素next数组的值默认是0
k = 1
for i in range(0,m):
if i+1>=m-i: #当左边的下标大于右边的下标则跳出循环
break
if p[0:i+1]==p[m-i-1:m]:
k=i+2
next.append(k)
我们只需在BF算法基础上改动,在失配时模式串退回到next[]数组指示位置。
实现代码如下:
def kmp_match(s,p,next):
nums1=len(s) #模式串中元素的个数
nums2=len(p)
i=j=0
while i!=nums1 and j!=nums2:
if c[i]!=p[j]:
j=next[j]
i+=1
else :
i+=1
j+=1
if j!=nums2 :
print("匹配失败,主串中不包含模式串")
return -1
else:
print(f"匹配成功,在主串中第{i-nums2+1}到第{i}")
return 0
def Get_next(p,next): #求模式串p的next数组的值
nums=len(p) #模式串中的元素个数
for m in range(1,nums): #从第1号元素开始算next数组值,第0号元素next数组的值默认是0
k = 1
for i in range(0,m):
if i+1>=m-i: #当左边的下标大于右边的下标则跳出循环
break
if p[0:i+1]==p[m-i-1:m]:
k=i+2
next.append(k)
def kmp_match(s,p,next):
nums1=len(s) #模式串中元素的个数
nums2=len(p)
i=j=0
while i!=nums1 and j!=nums2:
if s[i]!=p[j]:
j=next[j]
i+=1
else :
i+=1
j+=1
if j!=nums2 :
print("匹配失败,主串中不包含模式串")
return -1
else:
print(f"匹配成功,在主串中第{i-nums2+1}到第{i}")
return 0
s=input("请输入主串") #主串
p=input("请输入模式串") #模式串
p=list(p)
s=list(s)
next=[0]
Get_next(p, next)
kmp_match(s,p,next)
制作不易,点个赞再走吧。