KMP算法(下)
kmp 字符串模式匹配
克努特,莫里斯,普拉特
思想: 引入物理上的参照物的概念
宏观上看:
简单方法:
目标串(当成一个实物)是向左移动目标串s
也就是说,我也可以模式串(当成一个实物)向右移动模式串p
微观上看:
简单方法:
指针i从每次开始的位置向右移动1位
也就是说,指针j从每次开始的地方向左移动(未知)位置
充分利用前面已经匹配到的信息或者说字段
来进行向左移动模式串
分析:
我们之前KMP(上)只是移动S(目标串),效率太慢,时间复杂度o(m*n)
现在我们通过,移动s(目标串)和p(模式串)这种组合吧
(不严谨我自己起的名叫组合方便理解),来进行提高效率
重点来了:
跟着感觉走(不要问为什么,看完再发散思维,要不你会跟我一样被困好几天):
如果s[i]等于p[j],那么就继续往下走
如果s[i]不等于p[j],
判断移动s呢还是移动p呢
如果next[j]是大于0, 就移动j到next[j]
如果next[j]等于0 并且 j 不等于0,就移动到next[j]
如果j已经等于0了, 就移动i到i+1
这里为什么没有如果next[j]小于0呢?下面会你在代码中看到注释解释
自问1:
next[j]是个啥玩意?
自答1:
就是我们现在已经跑到j,那接下来就是比较下一次j指向的字符。
那么 next[j]就是下一次我们要比较的字符的位置或者说索引,也就是下次j指针应该等于的值
自问2:
你想表示下一次j指针的值,直接用一个数字表示不就完事了,
干嘛搞一个next[j]?
自答2:
是这样的,在匹配的过程中,在每个字符的比较都有可能出现不匹配的情况,
所以为了方便直接搞个名字为next的列表,存着每一个字符不匹配时,
下一次j应该等于的值
ps1:
(程序毕竟是程序,又不是人类,不能智能地判断,不匹配,再去判断next[j](大脑计算得到),再去考虑移动;
我们需要人类的思想,转成程序,就需要在判断next[j]的时候,先给计算机(cpu计算)生成一个next[j]的 值,这样计算机才能去读,然后继续比较。)
假设我已经会求next[j](也就是下一次j指针的值)了:
ok,接下来直接怼代码吧:
1 s = 'abcbabcabcf' # 匹配成功,在目标串的4位置 2 p = 'abcabc' # [0, 0, 0, 1, 2, 3] 3 4 # 人脑计算: 5 6 # p abcabc 7 # j 012345 8 # next[j] -100123 9 10 # next = [-1, 0, 0, 1, 2, 3] 11 # 因为指针 j = next[j] ,并且p[-1]是无意义的,最小也是p[0],所以将next优化 12 next = [0, 0, 0, 1, 2, 3] 13 14 def func(s, p): 15 """ 进行字符串匹配 """ 16 i = 0 17 j = 0 18 while 0 <= i < len(s) and 0 <= j < len(p): 19 if s[i] == p[j]: 20 i += 1 21 j += 1 22 else: 23 if next[j] > 0: 24 j = next[j] 25 elif next[j] == 0 and j != 0: 26 j = next[j] 27 else: 28 # 这里else 其实换成 elif j == 0 更好理解 29 # 因为此时j 指针已经指向0了,不能再向左移动了,才去移动指针i 30 i += 1 31 if j == len(p): 32 return '匹配成功,在目标串的{}位置'.format(i - j) 33 else: 34 return '匹配失败' 35 36 37 result = func(s, p) 38 print(result)
next数组怎么算?
可以参考以下两篇文章请跳转
https://www.cnblogs.com/yjiyjige/p/3263858.html
http://www.cnblogs.com/tangzhengyue/p/4315393.html
我就是参考这两篇博客领悟的;
如果你还是不太明白,可以看看我的,python版本,
(个人感觉python实现更清晰一点)
1 def get_next(p): 2 """ 获取next数组""" 3 next = [] 4 j = 0 5 k = -1 6 while j < len(p): 7 if k == -1 or p[j] == p[k]: 8 k += 1 9 else: 10 k = next[k] 11 next.append(k) 12 j += 1 13 return next
最后我测试了几组数据结果如下:
其中包含个各种字符串类型:
A代表一个或多个字符(不太确切,只可意会不可言传)
AA型 AxxxxxxA型 AAAxAA型 ~A型(就是每个字符全部一样) A型
1 s = 'abcbabcabcf' # 匹配成功,在目标串的4位置 2 p = 'abcabc' # [0, 0, 0, 1, 2, 3] 3 4 s1 = 'dddabcabadbabuh' # 匹配成功,在目标串的3位置 5 p1 = 'abcabadbab' # [0, 0, 0, 1, 2, 0, 0, 0, 1, 2] 6 7 s2 = 'hghsssass' # 匹配成功,在目标串的3位置 8 p2 = 'sssass' # [0, 1, 2, 2, 3, 2] 9 10 s3 = 'wwwwwabced' # 匹配成功,在目标串的5位置 11 p3 = 'abced' # [0, 0, 0, 0, 0] 12 13 14 s4 = 'awwwwabced' # 匹配成功,在目标串的5位置 15 p4 = 'abced' # [0, 0, 0, 0, 0] 16 17 s5 = 'bbssssssbbb' # 匹配成功,在目标串的2位置 18 p5 = 'ssssss' # [0, 1, 2, 3, 4, 5]

浙公网安备 33010602011771号