美文网首页
刷题套路之字符串匹配KMP要点

刷题套路之字符串匹配KMP要点

作者: Sonass | 来源:发表于2017-08-29 11:45 被阅读0次

KMP这个算法是专门为了解决字符串匹配和字符串子串匹配的一个经典算法。可以在O(n)内算出下列信息:

1. 两个字符串是否匹配,如果不匹配,最大长度是多少

2. 一个字符内是否有重复,如果有重复,重复了几次。

3.其他。。。

之前是跟小伙伴刷题的时候对这个问题产生了非常浓厚的兴趣。我自己在看的时候也看到了前辈们写的很好的文章们,比如

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

谷歌搜索第一名不是没有道理的。

我自己就借着他的文章总结一下。

1. 咱得引入一个概念 叫公共前缀后缀,啥叫公共前缀后缀呢?就是说,同一个字符串后缀等于前缀(不含自身)的连续子串。说概念太绕嘴,偷个图(来自引用链接)

2.引入下一个概念,最长公共前缀后缀。中文太长,英文叫border。就是所有的公共前缀后缀里面最长的那一个,还是那句话,重复不含自身。

我们把border这个概念用在全数组上,对于字符串下标i, border[i] 意味着S.substring(0,i+1)的最长公共前缀后缀。图里面border[5] = 1,因为最后一个A等于第一个A,border[6] == 2 因为最后两个AB等于第一个AB。这里看到了

公共前缀后缀的比较对象是不变的,永远是跟全字符串的前缀在比

公共前缀后缀不会突增,只会突减。

图中border[6] == 2了以后,border[7]==0了直接就。

那么问题来了,border每次回退,最少需要回退多少呢?(因为如果每次都是0,那跟暴力做法又有什么区别呢?)

3. 要证明的点如下:记一个字符串为S, S.substring(0,i) 所对应的border值是b,那么:

                a. border==0 没有意义 直接跳过,因为 0 说明没有前缀等于后缀。也就是说,回退的时候,如果已经回退到0了,就不用继续算了,直接i++

                b. 如果S.chatAt(i) != S.chatAt(b),最少需要回退的步数为b - border[b]。也就是说,下一个比较的就是S.chatAt(i) == S.chatAt(border[b])

                c. 如果S.charAt(i) == S.charAt(b),那么,i++; border(b) == b; b++;

                阿诺。。。本人的数学证明实在太差,我们就先不证明了,记住这个结论(也不难记)

然后 我们就知道border怎么算了,代码如下

好了,那现在要问了,会算border了怎么字符串匹配呀?

好说啊,你知道s.length, p.length < s.length,假设 p s 都是alphanumerical的,这么算就行

1. String buffer = s + "#" + p;  (s和p之间的分隔符可以使用任意特殊字符,保证border[indexOf(#)] == 0)

2. 算出border(buffer)[]的所有位

3. 如果任意时刻,border(buffer)[i] = p.length(); 发现匹配 


好了 一个比较显著的应用就是leetcode 459。求整个字符是否是整数个子串的重复。你想我们算border就是多少后缀等于前缀。这道题目不就自然想到了要用kmp的border来做么 

代码张这样,我参考了这个帖子

这里要说明的一点是把border算完以后我怎么知道有没有呢?我贴几个屏幕输出

我怎么知道上面一个是true,下面一个是false呢?

先再来一个例子

这就有点思路了吧?如果一个字符串是一个子串重复了k次整,那么到第k次的时候,假设下标为i 那么一定有

border[i] = (k-1) * pattern.length();

利用这个性质,我就可以有如下结论

1. 判断是否是整数次重复:看看是否S.length % (S.length - border[last entry]) == 0 

2. 如果1.为真,几次重复:S.length / (S.length - border[last entry])

还有一个很经典的例子是leetcode 214 这可是一个hard的题目

这道题目怎么做呢?问你只在最前面加字符,怎么加能够加成最短palindrome

是吧,写在这里就已经耍赖了。这题目其实是在问你这道题KMP思路怎么做?

首先咱先来个比较明显的事实:

如果一个String s 是palindrome,那么kmp(s + "#" + new StringBuilder(s).reverse().toString()) 应该完全匹配,最后index显示s.length()

那要不是呢?我们看看最后一个index显示什么

对于任意字符串s, kmp(s + "#" + new StringBuilder(s).reverse().toString()) 的最后一个index显示的是这个字符串从0到border[index]是回文串。

到这里 比我聪明的同学就可以想到

new StringBuilder(s.substring(border[index])).reverse().toString() + s 就是我们要的答案了!

有人(就是某个三十岁了还在刷题的아저씨)说不对啊,那就不能加在中间么?

abdac 中间加个d,前头加个c 不就是cadbdac了么?

看题!说了只能在最前加字符!

好吧。。。

代码如下

相关文章

  • 刷题套路之字符串匹配KMP要点

    KMP这个算法是专门为了解决字符串匹配和字符串子串匹配的一个经典算法。可以在O(n)内算出下列信息: 1. 两个字...

  • KMP算法

    KMP算法主要在于讨论字符串的匹配问题,在刷题LeetCode中遇到过几次,我觉得这个还是应该总结一下。KMP是一...

  • KMP字符串匹配

    KMP字符串匹配

  • KMP算法文章合集

    字符串的查找:朴素查找算法和KMP算法 暴力匹配算法与KMP算法(串的匹配) 字符串查找算法BF和KMP 字符串匹...

  • 09--KMP

    [toc] KMP算法原理 KMP思想 假设字符串abcdefgab和模式串abcdex,进行匹配,当匹配到x位置...

  • KMP算法

    字符串匹配算法之KMP KMP算法最主要的地方是求next数组,next数组保存的是当前失配节点(下标index)...

  • KMP

    KMP有什么用 KMP主要应用在字符串匹配上。 KMP的主要思想是「当出现字符串不匹配时,可以知道一部分之前已经匹...

  • 常见算法题之字符串

    1、KMP算法 参考:july大神的KMP博客细节不摆,该算法由暴力字符串来匹配,具体是由字符串匹配的暴力写法而来...

  • KMP算法的JS实现

    talk is cheap,show me the code: 参考链接: 阮一峰-字符串匹配的KMP算法[KMP...

  • leetcode字符串匹配算法之KMP算法

    本篇介绍一种高效的字符串匹配算法——KMP算法。 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J....

网友评论

      本文标题:刷题套路之字符串匹配KMP要点

      本文链接:https://www.haomeiwen.com/subject/fqhodxtx.html