注:本代码仅供理解KMP流程以及测试两者的效率比对使用,并无实用价值,虽然尽量做过测试了不过不保证完全正确,请读者自行分辨,本篇仅供吐槽与记录。
本来想写一些kmp的原理的记录不过在学习的过程中,在网上搜索到很多的文章各种图示图例都讲解的很详细了。所以,有浏览到的小伙伴直接去看这篇文章就好,开头就整理的很详细了,对着那几个超链接下来应该是会清楚了。
注:那里面那个三哥视频讲的很不错,感觉可以。
字符串的KMP算法–Python实现
主要是代码的混乱实在是让人走了很多弯路。
在此记录下这次的笔记顺便整理下各种代码,防止后面忘记了:
首先是对比的代码,BF暴力匹配:
import random
import time
random_str=''
base_str='abcdefghigklmnopqrstuvwxyz'
list_str=[]
i=0
'''
#超长串测试使用
#生成字符串
len_random=len(base_str)-1
while i<10000:
random_str += base_str[random.randint(0,len_random)]
i+=1
i=0
'''
#测试字符串
random_str='adfabfjabcxhadalkjlabfjaecjadfjaaaaaaaaaaaababfjabgxweoiulkjkabfjabcxhgoiuer'
#目标字符串
teststr='abfjabc'
len_test=len(teststr)
strnum=len(random_str)
#记录开始时间
starttime=time.time()
while i<strnum:
#判断首字符是否相等
if random_str[i] == teststr[0]:
j=0
while j<len_test:
#内部滚动比对
if random_str[i+j] != teststr[j]:
#不通过就直接继续下个大循环对比
break
elif j==len_test-1:
# 全部对比通过,记录位置
list_str.append(i)
#由于相等直接跳过这个已经找到的字段
i += len_test-1
break
j += 1
elif strnum-i<len_test:
#剩余长度小于字符串长度就不用进行搜索的
break
i+=1
#结束时间
endtime=time.time()
print('在'+str(strnum)+'长度文本中运行状况:')
print('程序的所需要的时间:'+str(endtime-starttime))
if list_str:
print('所寻找的字符串的位置:'+','.join(str(n) for n in list_str))
print('文中字符串的总数量'+str(len(list_str)))
else:
print('文本内未找到所寻找的字符串')
接下来是网上的各种python获取前缀数组的代码:
import time
import random
#我自己写的代码,基本符合那个三哥视频中的示例,也是奇葩了,网上找代码什么的还是要多测下
def get_next(st):
result_list=[]
for i in range(1,len(st)+1):
little_str=st[:i]
k=0
j=1
len_litter=len(little_str)
while j<len_litter:
if little_str[:j]==little_str[-j:]:
k=len(little_str[:j])
j+=1
result_list.append(k)
return result_list
def get_next2(str):
prefix = set()
res = [0]
for i in range(1, len(str)):
prefix.add(str[:i])
postfix = {str[j:i+1] for j in range(1, i+1)}
res.append(len((prefix & postfix or {''}).pop()))
return res
def get_next3(pattern):
next = []
pattern_list = list(pattern)
j = 0
i = 1
for s in range(len(pattern)): # 第一位一直是0
if s == 0:
next.append(0) # 看第i个和第j个字母是否相同,如果相同,则累加
# 同时i,j同时右移
elif (pattern_list[i] == pattern_list[j]):
next.append(j + 1)
j += 1
i += 1
# 如果不相同,则next为0,同时j也退回第一个位置,i继续前进一个位置
else:
next.append(0)
j = 0
i = s + 1
return next
'''
#生成字符串
#超长串测试使用
base_str='abcdefghigklmnopqrstuvwxyz'
random_str=''
len_random=len(base_str)-1
strnum=1000
# for i in range(1, strnum):
# random_str += base_str[random.randint(0,len_random)]
'''
#目标字符串
#random_str='abcdabca'
#random_str='aabaabaaa'
#random_str='abcdabd'
#random_str='abfjabc'
#random_str='aaaaaaaaaaaaaaaaaaaaaaab'
random_str='abcaby'
#记录开始时间
starttime=time.time()
print(get_next(random_str))
#结束时间
endtime=time.time()
print('程序get_next所需要的时间:'+str(endtime-starttime))
#记录开始时间
starttime=time.time()
print(get_next2(random_str))
#结束时间
endtime=time.time()
print('程序get_next2所需要的时间:'+str(endtime-starttime))
#记录开始时间
starttime=time.time()
print(get_next3(random_str))
#结束时间
endtime=time.time()
print('程序get_next3所需要的时间:'+str(endtime-starttime))
最后的kmp算法
def kmp(nativestr,targetstr):
#储存字符串中的数组位置的结果数组
list_num=[]
#储存next数组
list_next=get_next(targetstr)
j,i=0,0
len_tar=len(targetstr)
while i<len(nativestr):
#开始滚动搜索,j不为0说明目标串已经匹配部分并且跳跃但是还有一部分要比对完成
if nativestr[i]==targetstr[j] or j!=0:
while j<len_tar:
if j==len_tar-1 and nativestr[i+j]==targetstr[j]:
#字符串完整的查找出来了则开始继续寻找下一个字符串,并储存在结果数组中,并重置j
list_num.append(i)
#这里本来是要j+1 但是最后面i统一要+1循环 因此没有+1
i += j
j=0
break
elif nativestr[i+j] != targetstr[j]:
#开始运算j对应数位的最大前缀,并进行i的下一轮移动
i += j-1
j=list_next[j-1]
break
j+=1
# elif len(nativestr)-i<len_tar:
# #剩余的长度不够目标字符串长度的时候,退出
# break
i+=1
#空数组直接赋值-1,或根据情况自定义
if len(list_num)==0:
list_num=-1
return list_num
str1='adfabfjabcxhadalkjlabfjaecjadfjaaaaaaaaaaaababfjabgxweoiulkjkabfjabcxhgoiuer'
str2='abfjabc'
print('字符串所在位置:')
print(kmp(str1,str2))