我对KMP算法的理解

我对KMP算法的理解
翻译自: jBoxer's blog
    原文链接: http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/


    在过去的几天,我读了好几篇关于“利用KMP算法查找字符串的解释”的文章。出于某些原因,没有一篇是能令我接受和理解的。每当读到“前缀的后缀的前缀...”这些字眼时,我都有种拿头撞墙的冲动。。。

    最后,经过30分钟反复阅读CLRS的几段文章,我决定坐下来找一些关于文本串匹配的例子,并把过程写出来。现在我已经理解了KMP算法并且能解释它了。下面是我用自己的语言对它的解释,但是在这里我不会去阐释它为什么是高效的,网上已经有很多优秀的阐释了。我会按我所理解的去详细解释它是怎样运作的。

    部分匹配表

    KMP最关键的部分当属部分匹配表了,以前我不能理解KMP的最大障碍就是不能清楚的理解部分匹配表的含义。下面我会尽可能用简单的语言去解释部分匹配表。
    这是字符串“abababaca的部分匹配表:


    如果我有一个有8个字符的文本串(就当它是"abababca"好了), 我的部分匹配表就含有8个元素。如果我看的是匹配表最后一个元素,那么我关心的就是整个文本("abababca"), 如果我看的是第7个元素,我关心的就是文本串的前7个字符("abababc"),此时最后一个字符"a"是无关紧要的。同样,如果我看的是匹配表的第6个元素... 你懂的...注意,我还没有讨论匹配表的每个元素的含义,仅仅讨论了它们代表什么。

    现在,为了讨论他们的含义,我们首先要知道什么是文本串的前缀和后缀。
    前缀:一个文本串中去掉最后一个字符的所有组合("S", "Sn", "Sna", "Snap"都是文本串"Snape"的前缀)。
    后缀:一个文本串中去掉第一个字符的所有组合("e", "pe", "ape", "nape"都是文本串"Snape"的后缀)。

    知道了这些,我们就可以用一句话来解释部分匹配表的含义了:

        (子)文本串中前缀后缀最长公共元素的长度

    让我来解释一下,现在我们看匹配表的第3个元素,如上所述,此时我们关心的是文本串的前三个字符("aba"),"aba"中有两个前缀("a","ab")和两个后缀("a","ba"),其中前缀"ab"和两个后缀都不匹配,但是前缀"a"和后缀"a"匹配,因此,这个子串的前缀后缀最长公共元素的长度为1.

    让我们再看看第四个元素,这里我们关注的是文本串前四个字符("abab"),这个子串有3个前缀("a","ab","aba")和3个后缀("b","ab","bab"),这次,子串的子串的前缀后缀最长公共元素的长度为2(前后缀公有"ab").

    这个例子很有趣,下面我们再看匹配表的第5个元素,此时我们关心文本串的前5个字符("ababa"),子串有4个前缀("a","ab","aba","abab")和4个后缀("a","ba","aba","baba"),现在有两对前后缀是匹配的,分别是"a"和"aba",因为后者长于前者,所以"aba"赢了,这个子串的前缀后缀最长公共元素长度是3。

    接下来直接来到匹配表的第7个元素,对应的文本串子串是"abababc",虽然它有好几个前缀和后缀,但是显而易见,没有一对前后缀是匹配的,因此该子串的前缀后缀最长公共元素长度是0.

    最后来看匹配表的第8个元素,对应文本串"abababca",因为第一个和最后一个字符都是"a",所以该文本串的前缀后缀最长公共元素的长度至少是1了,对于长度大于等于2的前后缀,因为后缀包含字符 a c, 并且只有最后一个前缀"bababac"也包含a c,所以我们只需要看看长度为7的前缀("bababca")和后缀("abababc")的匹配情况就行了,不巧的是,显然二者并不匹配。所以文本串"abababca"的前缀后缀最长公共元素的长度为1.

    怎样使用部分匹配表?

    当我们找到部分匹配时,我们可以使用部分匹配表中的值来跳过(而不是重做不必要的旧比较)。 公式如下:
      如果找到长度partial_match_length的部分匹配,并且table[partial_match_length] > 1,
    我们可以跳过partial_match_length - table [partial_match_length - 1]个字符

    让我们用模式串"abababca"来匹配文本串"bacbababaabcbab",下面是模式串的部分匹配表:



    第一次两个串在这里匹配:



    部分匹配长度是1.

    部分匹配表中 table[partial_match_length - 1](即table[0])为0,所以我们不能跳过任何长度,只能一个接着一个比较,下一个部分匹配如下:



    部分匹配的长度是5,table[partial_match_length - 1](即table[4])为3,即我们可以跳过 partial_match_length-table[partial_match_length - 1](即是5-table[4] = 5-3=2)个字符:



    这时,模式串已经越界了,所以,匹配失败。
 
    结论

    现在你通过这篇文章掌握的,正如我之前说的,没有对KMP算法的详细解释和正式地证明,我展示的是我脑中KMP算法的流程,包括我学习它时感到困惑的一些细节。如果你有任何问题或者注意到了我描述时的错误(大概是我翻译的不好吧(*^_^*),jBoxer大神的原文还是够严谨的),请留下评论,或许我们能共同学到些东西。

原文完。原文链接





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值