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]

 

posted @ 2018-08-09 11:57  mhh4399  阅读(115)  评论(0)    收藏  举报