一.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. 如何手玩5h uoj215 果冻运输得到 AC

    最近在大力练习提答颓提答,听说果冻运输很好玩就来试试. 然后玩的停不下来 QAQ ... 于是开一篇博客写一下每个点的解法.(一个个手玩出来的..) 首先我们每次都算什么下一步完后会发生什么在大脑中演 ...

  2. cookie跨站脚本漏洞解决方案

    近日项目碰到一个跨脚本注入的问题: 这安全测评工具也是厉害了,直接将脚本注入到cookie里头,以前没有碰到这样的情况. 之前写过一篇文章过滤跨脚本注入的问题.<浅谈XSS攻击原理与解决方法&g ...

  3. git 查看、创建、切换、删除、重命名和推送分支

    1.查看本地所有分支:前面有 “*” 的是当前所处的分支 $ git branch test-A * test-B 2.查看本地和远程服务器的所有分支: $ git branch -a test-A ...

  4. shell中的输入输出和编程中的变量(shell 03)

    shell中的输入输出标准输入:键盘标准输出:终端显示器>> 追加是换行追加的echo -n 不尾随换行符 -e 启用解释反斜杠的转义功能 -E 禁用解释反斜杠的转义功能(默认) --he ...

  5. jenkins学习(1)

    (1)  按照JAVA, 增加环境变量JAVA_HOME     =      C:\Program Files\Java\jdk1.8.0_31  增加环境变量CLASS_PATH     =   ...

  6. python学习(十一) 文件和流

    11.1 打开文件 >>> f = open(r'c:\text\somefile.txt'),  第一个参数是文件名,必须有:第二个是模式:第三个参数是缓冲. 11.1.1 文件模 ...

  7. 进程之 Process join方法其他属性与进程Queue

    Process join方法 以及其他属性 在主进程运行过程中如果想并发地执行其他的任务,我们可以开启子进程,此时主进程的任务与子进程的任务分两种情况 情况一:在主进程的任务与子进程的任务彼此独立的情 ...

  8. oracle创建表空间、创建用户

    create user user_name identified by user_name create temporary tablespace user_name_temp tempfile '/ ...

  9. IDA Pro 权威指南学习笔记(五) - IDA 主要的数据显示窗口

    在默认配置下,IDA(从 6.1 版开始)会在对新二进制文件的初始加载和分析阶段创建 7 个显示窗口 3 个立即可见的窗口分别为 IDA-View 窗口.函数窗口和消息输出窗口 可以通过 View - ...

  10. python arp欺骗

    使用python构造一个arp欺骗脚本 import os import sys from scapy.all import * import optparse def main(): usage=& ...