跳至主要内容

字符串匹配的KMP算法

字符串匹配
是计算机的基本任务之一。

举例来说,有一个字符串"B・BC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?

许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。

这种算法不太容易理解,网上有很多解释,但读起来都很费劲。直到读到Jake Boxer的文章,我才真正理解这种算法。下面,我用自己的语言,试图写一篇比较好懂的KMP算法解释。

1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

2.

因为B与A不匹配,搜索词再往后移。

3.

就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

4.

接着比较字符串和搜索词的下一个字符,还是相同。

5.

直到字符串有一个字符,与搜索词对应的字符不相同为止。

6.

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

8.

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

9.

已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

10.

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

11.

因为空格与A不匹配,继续后移一位。

12.

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

13.

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

14.

下面介绍《部分匹配表》是如何产生的。

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

15.

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

16.

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。

(完)

对于梦想,我不放弃,不妥协!

评论

此博客中的热门博文

用Google Calendar写日记

Google的产品一直都是非常强大的。说它强大并不是因为Google公司有多么牛逼,而是它的产品如果你用心用的话总是会用处许多花样。与之前 强大的Gmail做成完全可以替代Google Reader的RSS阅读器 一样,Google calendar一样是个功能强大的产品。它除了大家常用的日历、任务表功能以外,还有一项并没多少人知道的记日记的功能。今天我就详细介绍一下怎样用Google Calendar记日记。 一、日记 日记想必许多人都曾经写过,以前自己买个本子,晚上没人的时候偷偷写,写完之后藏起来。每个人都有点小秘密,不过如果大家回头看一下自己的日记,一定会发现自己原来真的好傻。其实有这个想法,日记就没有白写。日记本来就是记录自己成长的log而已。既然是log,那么流水记事是一定的,时间准确,天气环境什么的都要计入。这样才能以最快的速度还原当天的情景。这其实就是日记的功能。 此外还有童鞋在日记本里记下自己的小梦想,比如买部好手机,到泰山旅游,或者追小丽什么的。这些如果记入日记本里,你会督促自己努力实现自己的目标。 总之,写日记是个很美妙的事情。如何让这件事情延续下去?就请出今天的主角Google Calendar先生。用它来记日记有以下几大优点: 1、日记的数据很安全,保存在Google机房,并且随时可以导出备份。 2、支持的平台很多,网页端,手机网页端,Android客户端,iOS客户端,让你随时都可以记日记。 3、速度很快,Google Calendar一直没有被GFW认证过。另外google的速度,你懂的。 4、仍然是安全,密码只有你自己知道,别人不可能破解。 5、在日历里以日程的方式添加记事,想写多少都可以。事情精确到时分。 6、可计入的内容很多,日期,地点,人物,时间,天气、照片,视频,文档等等。 7、可以提醒写日记。 8、强大的全文搜索 9、日历的查看方式,让你对日记更一目了然 10、可创建多个日记本,让你对自己的日记分类更明确 11、对每一种事件都可以添加不同的颜色,让你对自己一天忙的事情一目了然。 12、界面简洁,可以自定义背景 13、可以针对某一篇日记,某一个事件通过邮件的方式与人分享 缺点: 这种写日记的方式是对传统写日记的习惯的一种挑战,需要你逐渐的适应这种写日记的方式

自己打造Gmail RSS阅读器完美替代Google Reader(Gmail与ifttt的完美结晶)

Gmail RSS阅读器已经升级到2.0 GR挂掉的日期临近, Feedly 跟着不断的抽风,其他RSS阅读器残疾的功能让我们又一次怀念GR的日子,最近突发奇想,Gmail这么强大,可不可以变成RSS阅读器呢?答案当然是能的,并且与GR相媲美。 于是自己在G+的基友们帮助下打造了一个利用ifttt与Gmail配合的RSS阅读器,相当强大,优点多多,请各位达人指教: 1、收取速度国内延时20分钟之内,无漏收(国外源在五分钟之内) 2、只要Gmail不关,ifttt不关,那么你就可以一直使用下去 3、 内容不过滤,安全 4、翻墙订阅没问题,图片照常显示 5、用Gmail的标签以及关键词过滤管理订阅源,与gr的边栏一样的效果 6、 可以加星标,另加标签来保存重要的文章 7、Gmail的访问速度 8、 Gmail的各种访问平台,跨平台使用。Android,web,桌面,windows,mac,iPhone,iPad,Linux等等,只要能用Gmail就可以用Gmail RSS 阅读器。 9、Gmail的IMAP设置可以在移动端有选择性的收取某个标签的订阅源。 10、 以往的订阅条目全部保存 11、订阅更新可以通过邮箱提醒 12、 全文搜索 13、对收取过程的全程监控 14、可以邮件编辑后转发以此来共享,目前evernote,Blogger,QQ空间支持邮件发布 15、 支持快捷键并且与GR的差不多 16、利用Gmail实验室里的预览窗格可以增加一种阅读方式 17、可以标为已读 18、可以自动翻到下一条目 19、可以删除无聊的条目,比如博主的测试条目 20、客户端有没有都可以用 还有更多的功能供大家来发现 当然也有缺点: 1、添加订阅比较麻烦:需要在ifttt添加触发,在Gmail里面添加过滤,标签 2、邮箱界面的阅读有些童鞋不喜欢,不过Gmail可以换背景的说 打造教程: 一、总体思路 这个Gmail阅读器主要原理是利用ifttt的Feed 应用抓取订阅源的文章更新,然后利用设定好的触发器,通过Gmail应用将这个更新作为一封Html邮

Gmail RSS阅读器2.0升级版

用Gmail+ifttt的完美搭配做成了完全可以替换Google Reader的Gmail RSS阅读器1.0,教程在此: http://james-sun.blogspot.com/2013/05/google-readergmail-rssgmailifttt.html 但是经过一个星期网友们的建议之后,发现了许多不够完美的地方,博主总结了各个建议打造出来了Gmail RSS阅读器2.0. 这次的升级log有以下几点: 1、更加精准方便的收件人地址过滤,解决关键词过滤的各种缺点 2、增加Feedburner的功能,解决依靠ifttt的局面 3、颜色标签,更加方便的管理Feed更新 4、增加回复、转发等共享及保存的功能 5、增加稍后阅读及笔记等标签,让Gmail不仅可以做RSS阅读器,也可以做笔记软件。 6、原来的20大功能一个不少 本篇文章是基于你已经用过 Gmail RSS阅读器1.0 或者明白它的工作原理,因此如果你不是,那么先点链接温习一下吧! 一、精准方便的收件人地址过滤 如果你拥有一个Gmail邮箱,那么你就拥有了无限个邮箱地址。假如你的邮箱是 google@gmail.com , 那么 google+yueguang@gmail.com ; google+QQ@gmail.com ;google+com+cn等等这样格式的邮箱地址都是你的,并且凡是发送到这些邮箱的邮件通通进入你的 google@gmail.com 这个邮箱。 Gmail的过滤器有个通过收件人过滤的规则,在这个规则里填写这个收件人( google+qq@gmail.com )的地址,那么凡是发送到这个地址的邮件将服从过滤器的命令,对其加标签,跳过收件箱。 此外, g.oo.g.le@gmail.com 这种格式的邮箱地址也是属于你的。 g.oo.gle+qq@gmail.com 这类格式也是你的。然而,每个不同的邮箱地址在过滤器面前却都是不同的。就是这么神奇。 多个收件地址过滤的方法: 如果你需要将许多个feed更新归类到一个标签,那么在使用收件人过滤方法的时候,邮箱地址的填写格式一定要这样填:( google+aa@gmail.com )|( google+bb@gmail