最近又想起了KMP算法,原来一直没搞明白工作原理,现在总算是开点窍了,推荐大家看这篇文章,写的很简单易懂

推荐理由:简单明了,是我看过介绍KMP算法流程的所有文章中,最易懂的一篇(这篇文章仅仅是介绍了KMP算法的工作流程,并没有介绍KMP算法为什么当初这么设计!)

原文地址:http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/

===================================================================================

最近我一直在研读各种关于Knuth-Morris-Pratt这种字符串匹配算法的介绍文章。但种种原因导致没有一种我能够真正理解的简简单单的介绍。

最终,在一遍又一遍的读完算法导论中相同段落之后,我决定静下心来好好写一些例子,把自己所理解的KMP算法写出来。现在,我总算明白了这个算法,也能解释明白它了!下面我就用我自己的大白话给大家姐是个明白!不过你需要注意一下,我并不打算解释KMP算法为什么比其他的字符串匹配算法高效,因为在这里已经解释的够清楚的了,而我要做的工作是用自己的话,介绍一下KMP算法的工作流程。

部分匹配表(The Partial Match Table)

KMP的精髓无疑就是部分匹配表了。明不明白KMP本质上就在于明不明白部分匹配表里所有数值的含义,我会尽可能的用简单明了的话进行解释。

下面这个是”abababca”这个模板(pattern)的部分匹配表:

char:   | a | b | a | b | a | b | c | a |

index:  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |

value:  | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |

如果我有一个8个字符的模板(这里就拿”abababca”来举例子),我的部分匹配表会有8列。如果我此时此刻正关注于模板的第八列,即最后一列,那意味着我考虑到了整个模板,(即”abababca”);如果我此时此刻正关注于模板的第七列,那意味着我当前仅仅考虑到了整个模板的前七位,(即”abababc”),此时第八位(”a”)是无关的,不用理睬;如果我此时此刻正关注于模板的第六列,那意味着……看到这里你应该已经明白我的意思了。目前我还没有提到部分匹配表每列数据的含义,在这里仅仅是先交代一下部分匹配表的大概。

现在,为了解释刚刚提到的每列数据的含义,我们首先要明白什么是最优前缀(proper prefixes)什么是最优后缀(proper
suffixes)

最优前缀:一个字符串中,去除一个或多个尾部的字符得到的新的字符串就是最优前缀。例如,”S”,”Sn”, ”Sna”, ”Snap”,都是”Snape”的最优前缀。

最有后缀:一个字符串中,去除一个或多个首部的字符得到的新的字符串就是最有后缀。例如,”agrid”, ”grid”, ”rid”, ”id”, ”d”都是 ”Hagrid”的最优后缀。

明白了这两个概念以后,我现在就可以用一句话概括部分匹配表里每列数据的含义了:

模板(子模板)中,既是最优前缀也是最优后缀的最长的字符串的长度。

下面我来验证一下这句话。还拿”abababca”这个模板举例来讲,假设当前我正在关注于模板的第三列数据,如果你还记得我在前文提到的,你应该知道这意味着我们目前仅仅关心前三个字母(”aba”)。在”aba”这个子模板有两个最优前缀(“a”和”ab”),有两个最优后缀(“a”和”ba”),不难看出,最优前缀与最优后缀中,相同的只有”a”这一个,那么此时此刻既是最优前缀也是最优后缀的最长的字符串的长度就是1了。

我们不妨再试一试第四列,第四列的话我们应该是关注于前四位字母(“abab”),这里可以看出有三个最优前缀(“a”,”ab”,”aba”)和三个最有后缀(“b”,”ab”,”bab”),这一次”ab” 既是最优前缀也是最优后缀,并且长度为2,是最优前缀与最优后缀交集里最长的,因此,部分匹配表的第四列的值赋值为2。

如果你还没试够,那你可以再试试第五列的情况,也就是关注于前五位,即“ababa”。这里不难得到4个最优前缀(“a”, “ab”, “aba”, and “abab”)和四个最有后缀(“a”, “ba”,“aba”, and “baba”),其中公共的有两个(“a”和“aba”),对比一下不难看出”aba”长度为3,且比”a”要长,所以部分匹配表的第五列赋值为3。

跳过中间的,直接来看第七列吧,此时只考虑前七位字母(“abababc”)。即使不一一枚举出所有的最优前缀与最优后缀也不难看出这两个集合之间不会有任何的交集。因为任何的最有后缀都以”c”结尾,但没有任何最优前缀是以”c”结尾的,所以没有符合要求的,因此第七列赋值为0。

最后,让我们看看第八列的情况,也就是考虑到整个模板的时候(abababca)。不难看出最优前缀与最有后缀都以”a”开头以”a”结尾,所以第八列的值至少是1。而事实上1就是最终结果了,所有大于等于2的长度中,所有最有后缀都包含”c”,但只有”abababc”这一个最优前缀包含”c”,而七位的最优后缀”bababca”并不与其一致,所以第八列最终赋值为1。

如何使用部分匹配表

在已经匹配到部分字符后,如果发现下一个字符不再与模板匹配,此时通过使用部分匹配表就可以快速的跳过一些字符,从而避免冗余的比对提升效率。具体的使用方法可以用下面的话来解释:

如果已经匹配到的部分字符串的长度为partial_match_length,且字符串中下一个字符不再与模板匹配的情况下,如果table[partial_match_length] > 1那么我们可以跳过partial_match_length- table[partial_match_length - 1]这么多个字符。

还是拿”abababca”来举例,如果我们用这个模板来匹配” bacbababaabcbab”的话,我们的部分匹配表应该是这样的:

char:   | a | b| a | b | a | b | c | a |

index:  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |

value:  | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |

第一次匹配的时候是这里:

ba cbababaabcbab

|

a bababca

此时partial_match_length值为1,对应的table[partial_match_length - 1] 即 table[0]为0,所以这种情况下我们不能跳过任何字符。下一次匹配的时候是这里:

bacb ababa abcbab

| | | | |

ababa bca

此时partial_match_length值为5,对应的table[partial_match_length - 1] 即 table[4]为3,这意味着我们可以跳过partial_match_length- table[partial_match_length - 1] ,即 5 - table[4] 即 5 - 3 亦即 2个字符。

// x 表示跳过一个字符

bacb ababa abcbab

xx | | |

aba babca

此时partial_match_length值为3,对应的table[partial_match_length- 1] 即 table[2]为1,这意味着我们可以跳过partial_match_length - table[partial_match_length - 1] ,即 3 - table[2]即 3 - 1 亦即 2个字符。

// x表示跳过一个字符

bacbababa abcbab

xx |

a bababca

此时此刻,模板长度大于所剩余的目标字符串长度,所以不可能会有匹配了。

结论

懂了没!正如我一开始所承诺的,没有纠结的解释也没有枯燥的证明。我就是这么理解的KMP。如果你有任何疑问或者发现我这篇文章哪里写错了,请给我留言,共同成长,与君共勉!

KMP算法的工作流程介绍的更多相关文章

  1. Storm 中什么是-acker,acker工作流程介绍

    概述 我们知道storm一个很重要的特性是它能够保证你发出的每条消息都会被完整处理, 完整处理的意思是指: 一个tuple被完全处理的意思是: 这个tuple以及由这个tuple所导致的所有的tupl ...

  2. KMP算法的优化与详解

    文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...

  3. 字符串匹配KMP算法详解

    1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...

  4. KMP算法详细分解

    1. 引言 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的 ...

  5. 从头到尾测地理解KMP算法【转】

    本文转载自:http://blog.csdn.net/v_july_v/article/details/7041827 1. 引言 本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP ...

  6. 很详尽KMP算法(厉害)

    作者:July时间:最初写于2011年12月,2014年7月21日晚10点 全部删除重写成此文,随后的半个多月不断反复改进.后收录于新书<编程之法:面试和算法心得>第4.4节中. 1. 引 ...

  7. KMP算法(转)

    KMP算法 在介绍KMP算法之前,先介绍一下BF算法. 一.BF算法 BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个 ...

  8. KMP算法 --- 在文本中寻找目标字符串

    KMP算法 --- 在文本中寻找目标字符串 很多时候,为了在大文本中寻找到自己需要的内容,往往需要搜索关键字.这其中就牵涉到字符串匹配的算法,通过接受文本和关键词参数来返回关键词在文本出现的位置.一般 ...

  9. 转载 - KMP算法

    出处:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html KMP算法 在介绍KMP算法之前,先介绍一下BF算法. 一. ...

随机推荐

  1. Oracle 11g使用DML Error Logging来避免bulk insert故障

    当使用带有子查询的insert语句来加载数据时如果出现错误.系统会终止该语句并回滚整个操作.这是非常消耗时间和资源的操作.如果insert这样的语句可以使用DML Error Logging功能来避免 ...

  2. Jquery EasyUI Treegrid按需加载子集

    项目说明,要一个有权限并且按需加载的树形列表. jeasyui网址 CSS <!--添加树状控件--> <link rel="stylesheet" type=& ...

  3. java与C语言在字符串结束符上的区别

    综述:在C语言中字符串或字符数组最后都会有一个额外的字符‘\0’来表示结束,而在java语言中没有结束符这一概念.具体见下面分析. 1. C 语言 在C语言中字符串和字符数组基本上没有区别,都需要结束 ...

  4. Spark 概念学习系列之从物理执行的角度透视spark Job(十七)

    本博文主要内容:  1.再次思考pipeline 2.窄依赖物理执行内幕 3.宽依赖物理执行内幕 4.Job提交流程 一:再次思考pipeline 即使采用pipeline的方式,函数f对依赖的RDD ...

  5. Navicat Premium v12.0.23.0 安装,使用激活码激活

    1 下载 可以直接官网下载安装包,也可以直接到我的云盘下载 下载地址:https://pan.baidu.com/s/1apwU9cIKBTr-z0CuJEJ9gg 文件包中包含下面的文件: 2 安装 ...

  6. log4j的AppenderLayout格式符

    %p:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL. %d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd ...

  7. Maven工程红色感叹号,且工程无红叉错误

    很可能是jar包不对,可以将maven库里的jar包删除,从 http://mvnrepository.com/ 根据jar包版本号下载到本地maven库,并在pom.xml里引入jar依赖 这次ja ...

  8. 细说C#中的序列化与反序列化的基本原理和过程

    虽然我们平时都使用第三方库来进行序列化和反序列化,用起来也很方便,但至少得明白序列化与反序列化的基本原理. 懂得人就别看了! 注意:从.NET Framework 2.0 开始,序列化格式化器类Soa ...

  9. 全局数据 GetGlobalDataSet

    /// <summary> /// 获取全局数据 /// </summary> /// <returns></returns> public DataS ...

  10. DW网页制作,数学,数据库管理

    数学(函数关系的建立) 函数关系:确定性现象之间的关系常常表现为函数关系,即一种现象的数量确定以后,另一种现象的数量也随之完全确定,表现为一种严格的函数关系. 如:记为y=f(x),其中x称为自变量, ...