KMP 算法

内容会持续更新,有错误的地方欢迎指正,谢谢!

举例:有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含目标字符串”ABCDABD”?

别忘了,空格也是字符。Knuth-Morris-Pratt算法(KMP)是最常用的解决该类问题的算法。

蛮力字符串匹配

看到这题,我们的第一个想法是蛮力匹配:

  1. 先定义两个下标变量i和j分别表示原字符串和目标字符串的首字符下标;
  2. 若两个下标变量对应的字符相同,则一起往后走一步;
  3. 否则,原字符串的i=i-j+1,就指向了原字符串第一个A(见下图)的下个字符B;此时,j要重新置为0;
  4. 怎样判断匹配成功?当j==目标字符串的长度则返回true,否则返回false。
    这里写图片描述

蛮力法效率很差,因为你要把 “搜索位置” 移到已经比较过的位置,重比一遍,而KMP用部分匹配表解决了这个浪费。蛮力字符串匹配的时间复杂度为 O(nm),其中 n 为原字符串的长度,m 为目标字符串的长度。很明显,这样的时间复杂度很难满足我们的需求。接下来进入时间复杂度为 O(n+m) 的KMP字符串匹配。

KMP字符串匹配

站在巨人的肩膀上,请见:字符串匹配的KMP算法
不过就是新加了几个概念:

  1. 前缀:除了最后一个字符外,一个字符串的全部头部组合。
  2. 后缀:除了第一个字符以外,一个字符串的全部尾部组合。
    以”ABCDABD”为例:
    “A”的前缀和后缀都为空集,共有元素的长度为0;
    “AB”的前缀为[A],后缀为[B],共有元素的长度为0;
    。。。
    “ABCDAB”的前缀为[A][AB][ABC][ABCD][ABCDA],后缀为[B][AB][DAB][CDAB][BCDAB],最长的共有元素为”AB”,长度为2。
  3. 部分匹配值:前缀和后缀的最长的共有元素的长度。
  4. 部分匹配表:装部分匹配值,方便看呗。
    这里写图片描述
    (写代码的时候,第一个A的部分匹配值为-1,因为第一个字符就不匹配,就要后移一位)

当遇到不匹配的字符时,需移动的位数=已匹配的字符个数 - 最后一个匹配字符对应的部分匹配值
这里写图片描述
此时,已匹配的字符个数为6,最后一个匹配字符B对应的部分匹配值为2,那就移动4位,如下图:
这里写图片描述

KMP时间复杂度分析

得到部分匹配表,需要在目标字符串中遍历一次,时间复杂度为O(m)
利用部分匹配表在原字符串中遍历一次,就可完成字符串匹配,时间复杂度为O(n)
两者相加为 Θ(m+n),所以平均时间复杂度为 Θ(m+n)。

参考

【1】The Knuth-Morris-Pratt Algorithm in my own words
http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/
【2】字符串匹配的KMP算法
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值