一.KMP算法是如何针对传统算法修改的

用模式串P去匹配字符串S,在i=6,j=4时发生失配:

---------------------------------------------------------------------

i=6

S: a   b   a   b   c   a   d   c   a   c   b   a   b

P:           a   b   c   a   c

j=4

---------------------------------------------------------------------

此时,按照传统算法,应当将P的第 1 个字符 a(j=0) 滑动到与S中第4个字符 b(i=3) 对齐再进行匹配:

---------------------------------------------------------------------

i=3

S: a   b   a   b   c   a   a   d   a   c   b   a   b

P:             a   b   c   a   c

j=0

---------------------------------------------------------------------

这个过程中,对字符串S的访问发生了“回朔”(从 i=6 移回到 i=3)。

我们不希望发生这样的回朔,而是试图通过尽可能的“向右滑动”模式串P,让P中index为 j 的字符对齐到S中 i=5 的字符,然后试图匹配S中 i=6 的字符与P中index为 j+1 的字符。

在这个测试用例中,我们直接将P向右滑动3个字符,使S中 i=5 的字符与P中 j=0 的字符对齐,再匹配S中 i=6 的字符与P中 j=1 的字符。

---------------------------------------------------------------------

i=6

S: a   b   a   b   c   a   d   c   a   c   b   a   b

P:                     a   b   c   a   c

j=0

---------------------------------------------------------------------

二.求KMP算法中的next

举例说明:

按上述定义给出next数组的一个例子:

j         0  1  2  3  4  5  6  7

P        a   b  a  a  b  c  a   c

next[j]  -1  0  0  1  1  2  0  1

查找对称串
申明一下:下面说的对称不是中心对称,而是中心字符块对称,比如不是abccba,而是abcabc这种对称。
详解:
将j导入next函数,即可求得, j=0时,next[]=-1; j=1时,k的取值为(,1)的开区间,所以整数k是不存在的,那就是第三种情况,next[]=; j=2时,k的取值为(,)的开区间,k从最大的开始取值,然后带入含p的式子中验证等式是否成立,不成立k取第二大的值。现在是k=,将k导入p的式子中得,p0=p1,即“a”=“b”,
显然不成立,舍去。k再取值就超出范围了,所以next[]不属于第二种情况,那就是第三种了,即next[]=; j=3时,k的取值为(,)的开区间,先取k=,将k导入p的式子中得,p0p1=p1p2,不成立。 再取k=,得p0=p2,成立。所以next[]=; j=4时,k的取值为(,)的开区间,先取k=,将k导入p的式子中得,p0p1p2=p1p2p3,不成立。 再取k=,得p0p1=p2p3,不成立。 再取k=,得p0=p3,成立。所以next[]=;

……

在已知next数组的前提下,字符串匹配的步骤如下:

i 和 j 分别表示在主串S和模式串P中当前正待比较的字符

在匹配过程中的每一次循环,若,i 和 j 分别增 1,

else,j 退回到 next[j]的位置,此时下一次循环是相比较。

void getnext(int *next, char *p)
{
int j = , k = -;
next[] = -;
while(j < lenp-1)//-1
{
if(k == - || p[j] == p[k])
{
j++;
k++;
next[j] = k;//当j==0时,已经求出了next[1]的值,所以j<lenp-1
}
else
k = next[k];
}
}

.getNext函数的进一步优化

注意到,上面的getNext函数还存在可以优化的地方,比如:

i=3

S: a   a   a   b   a   a   a   a   b

P: a   a   a   a   b

j=3

此时,i=3、j=3时发生失配,next[3]=2,此时还需要进行 3 次比较:

i=3, j=2;

i=3, j=1;

i=3, j=0。

而实际上,因为i=3, j=3时就已经知道a!=b,而之后的三次依旧是拿 a 和 b 比较,因此这三次比较都是多余的。

此时应当直接将P向右滑动4个字符,进行 i=4, j=0的比较。

一般而言,在getNext函数中,next[i]=j,也就是说当p[i]与S中某个字符匹配失败的时候,用p[j]继续与S中的这个字符比较。

如果p[i]==p[j],那么这次比较是多余的(如同上面的例子),此时应该直接使next[i]=next[j]。

void getNextUpdate(const std::string& p, std::vector<int>& next)
{
next.resize(p.size());
next[] = -; int i = , j = -; while (i != p.size() - )
{
//这里注意,i==0的时候实际上求的是nextVector[1]的值,以此类推
if (j == - || p[i] == p[j])
{
++i;
++j;
//update
//next[i] = j;
//注意这里是++i和++j之后的p[i]、p[j]
next[i] = p[i] != p[j] ? j : next[j];
}
else
{
j = next[j];
}
}
}

假定p.size()为m,分析其时间复杂度的困惑在于,在while里面不是每次循环都执行 ++i 操作,所以整个while的执行次数不一定为m。

换个角度,注意到在每次循环中,无论 if 还是 else 都会修改 j 的值且每次循环仅对 j 进行一次修改,所以在整个while中 j 被修改的次数即为getNext函数的时间复杂度。

每次成功匹配时,++i; ++j; , 由于 ++i 最多执行 m-1 次,故++j也最多执行 m-1 次,即 j 最多增加m-1次;

对应的,只有在 j=next[j]; 处 j 的值一定会变小,由于 j 最多增加m-1次,故 j 最多减小m-1次。

综上所述,getNext函数的时间复杂度为O(m),

若带匹配串S的长度为n,则kmp函数的时间复杂度为O(m+n)。(有待验证)

四、kmp的应用优势

①快,O(m+n)的线性最坏时间复杂度;

②无需回朔访问待匹配字符串S,所以对处理从外设输入的庞大文件很有效,可以边读入边匹配。

大部分转自GoAgent

http://www.cnblogs.com/goagent/archive/2013/05/16/3068442.html

KMP算法细讲(豁然开朗)的更多相关文章

  1. KMP算法的理解

    ---恢复内容开始--- 在看数据结构的串的讲解的时候,讲到了KMP算法——一个经典的字符串匹配的算法,具体背景自行百度之,是一个很牛的图灵奖得主和他的学生提出的. 一开始看算法的时候很困惑,但是算法 ...

  2. 【讲●解】KMP算法

    KMP算法 我们小组负责讲这个... 术语与规定 为了待会方便,所以不得不做一些看起来很拖沓的术语,但这些规定能让我们更好地理解\(KMP\)甚至\(AC\)自动机. 字符串匹配形式化定义如下: 假设 ...

  3. 讲不明白自杀系列:KMP算法

    算法:KMP排序 算法分析 KMP算法是一种快速的模式匹配算法.KMP是三位大师:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,所以取首字母组成KMP. 少部分图片来自孤~影 ...

  4. 详讲KMP算法

    两个字符串: 模式串:ababcaba 文本串:ababcabcbababcabacaba KMP算法作用:快速在文本串中匹配到模式串 如果是穷举法的方式: 大家有发现,这样比效率很低的. 所以就需要 ...

  5. Python 细聊从暴力(BF)字符串匹配算法到 KMP 算法之间的精妙变化

    1. 字符串匹配算法 所谓字符串匹配算法,简单地说就是在一个目标字符串中查找是否存在另一个模式字符串.如在字符串 "ABCDEFG" 中查找是否存在 "EF" ...

  6. KMP算法

    KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...

  7. Java数据结构之字符串模式匹配算法---KMP算法

    本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基 ...

  8. KMP 算法

    KMP 是一个字符串匹配算法.之所以称之为KMP 是因为这个算法是由Knuth.Morris.Pratt三个提出来的. 这个算法能干什么呢 ? 我想到的有三个: 1. 告诉你一个串是否是另外一个串的子 ...

  9. (原创)详解KMP算法

    KMP算法应该是每一本<数据结构>书都会讲的,算是知名度最高的算法之一了,但很可惜,我大二那年压根就没看懂过~~~ 之后也在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么 ...

随机推荐

  1. matlab中的开方sqrt用牛顿迭代法实现的代码

    function kaifang = KAIFANG(a)g0=a/2;g1=(g0+a./g0)/2;for i=0 : 299g0=g1;g1=(g0+a./g0)/2;endkaifang = ...

  2. iis部署网页时应该避免的特殊端口

    1 tcpmux 7 echo 9 discard 11 systat 13 daytime 15 netstat 17 qotd 19 chargen 20 ftp data 21 ftp cont ...

  3. 简述负载均衡和 CDN 技术

    曾经见到知乎上有人问“为什么像facebook这类的网站需要上千个工程师维护?”,下面的回答多种多样,但总结起来就是:一个高性能的web系统需要从无数个角度去考虑他,大到服务器的布局,小到软件中某个文 ...

  4. bzoj 1000 A+B Problem (And also my first experience of Emacs)

    problem:https://www.lydsy.com/JudgeOnline/problem.php?id=1000 This is my first code under Emacs! #in ...

  5. 插入排序的JavaScript实现

    思想 每次在现有已经排好的数组的基础上排入一个新的数组项. 先把第一项看做是已经排好的,第二项应该排在第一项之前还是之后呢?当前两项排好后,第三项应该排在这已排好的两项的之前还是之后还是中间呢?当前三 ...

  6. css3作3D旋转视频展示

    代码如下: <!doctype html> <html lang="en"> <head> <meta charset="UTF ...

  7. 找回mysql root用户的密码

    1.停掉mysql服务ps -ef | grep mysqldkill -9 mysql进程的pid2.vi /etc/my.cnf找到[mysqld]在下面添加一行skip-grant-tables ...

  8. Quartz.net 2.x 学习笔记02-Quartz.net 2.x在MVC站点中结合Log4net的使用

    Quartz.net 2.x在MVC站点中结合Log4net的使用 首先新建一个MVC的空站点: 第二步,添加Quartz.net的引用 在搜索处输入quartz.net搜索安装即可(目前是2.3) ...

  9. 1126 Eulerian Path

    题意:若图是连通图,且所有结点的度均为偶数,则称为Eulerian:若有且仅有两个结点的度为奇数,则称为semi-Eulerian.现给出一个图,要我们判断其是否为Eulerian,semi-Eule ...

  10. python开发面向对象基础:组合&继承

    一,组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合      人类装备了武器类就是组合 1.圆环,将圆类实例后传给圆环类 #!/usr/bin/env python #_*_ ...