KMP(课下总结)

在讲KMP之前请允许我回顾一下BF算法,那么什么叫BF算法呢?就是最普通的暴力,请见下方的代码

 1 int return_pos(string S,string P) {
 2     int i = 0,j = 0,k = 0;
 3     int Slen = S.length();
 4     int Plen = P.length();
 5     while (i < Slen && j < Plen) {
 6         if (S[i + k] == P[j]) {
 7             ++k,++j;
 8         }
 9         else {
10             i++,j = 0,k = 0;
11         }
12     }
13     if (j == Plen) return i;
14     return -1; // 返回-1表示不匹配
15 }

通过上面的代码,估计你已经知道了什么叫做BF了吧;(在看不懂上面代码之前不要往下看,看了你也不懂,最好弄懂每个字母代表的含义,这是我没有加注释的原因之一,原因之二呢,实在是不想打注释。。。O(∩_∩)O哈哈~。。。)

好的,接下来就到我们的重点了,KMP算法;

正如上面的BF算法所示,当每一次不匹配的时候,i回到原来位置,然后i++,j= 0;可以看出每一次的i回到原来位置,j 赋值为0,

是不是太麻烦了,如果串的长度太大,你懂得。。。。。

好了,KMP这个时候来了,他们就想啊,如果我们不让i回到原来位置(也就是说不要k(BF中的)了),只让j在动,能不能实现这个字符串匹配呢,

wow,最后他们成功了,(是时候该骂他们了,就是因为他们成功了,我们还要学这破算法,学就学吧,还TMD那么难理解)

废话不说,请看下方。

那么我们来个例子,我们设S串ababc,P串为abc。好的,那么我们先看一下,BF是怎么实现的。。。。。

S       a(babc)     ab(abc)    aba(bc)

P       a(bc)         ab(c)        abc 

(我擦。。。不匹配,白忙活了,看我回溯。。。)

S      ab(abc)

P        a(bc)

(我擦来。。。还是不匹配,我再回溯。。。)

S       aba(bc)    abab(c)     ababc

P           a(bc)        ab(c)         abc

(要西,终于匹配了。)

接下来看KMP是怎么做的

S       a(babc)     ab(abc)    aba(bc)

P       a(bc)         ab(c)        abc 

(我擦。。。不匹配,白忙活了,看我next。。。)

S       aba(bc)       abab(c)   ababc

P           a(bc)           ab(c)       abc

(牛逼吧。。。。。)

通过看这组样例,估计大家已经觉得KMP的神奇之处了。(难道你不想问我next是干么的吗?什么?你不问?那我也要跟你说,接着往下看。。。)

那么重点来了。下面解释一下啊next数组的含义,这也是KMP不太好理解的地方。

  令原始串为S(是S,不是P)长度为n,模式串为T(是T!是T!!)长度为m;

  我们假设目前匹配到如下位置

      S0,S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn

                    T0,T1,...............Tj-1,Tj,............

  (绿色的部分为匹配部分,红色为不匹配的部分)估计你看出来了吧,恰好Si和Tj的时候,失配,那么我们将不会动i的位置。动用next神器。

1)如果模式串右移1位(为了简单,就让我假设1位吧),即 next[j] == j - 1(对吧),好,那看看接下的KMP是怎么做的

      S0,S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn

                    T0,T1,...............Tj-1,Tj,............

                    T0,T1.................,Tj-1,Tj.............

   (蓝色的部分是即将要看看是否匹配的字符) 看到了吧,是不是省下了好多不必要的麻烦。

   (难道你没有问题要问我吗?什么?没有?真没有?你确定?那我问你一个问题,请看下方)

      S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn

                T0,T1...........,Tj-2,Tj-1,Tj.............

   (好,请回答我,为什么绿色的部分是匹配的呢?什么?我说的,我什么时候说的?看到粉色的字符了吗?原来Si-j与T0匹配,哈哈,回答不出来吧,让我们

  回想一下next的作用,为什么next[j] == j - 1,因为T0,T1......Tj - 2  ==  T1,T2........Tj - 1。。。。看到这个是不是你想到什么东西,对,就是老师上课讲的那个令人头痛的公式,没记错的的话,应该在PPT28页,你可以回去看看,现在应该可以看的懂了。)

   通过这个例子,我现在就可以告诉你了,next[j] 就是记录T[0...j-1]中前缀和后缀相等的最大长度。

那么接下来,我就敲一遍KMP的核心算法。请看下方。。。

 1 void Get_next(string P,int* next) {
 2     int i,j;
 3     next[i = 0] = j = -1;
 4     int len = P.length();
 5     while (i < len - 1) {
 6         if (j == -1 || P[i] == P[j]) {
 7             next[++i] = ++j;
 8         }
 9         else j = next[j];
10     }
11 }

代码给你了,估计你能看得懂吧。

那么我就做个总结吧。。。归根究底呢,KMP算法的本质便是:针对匹配的模式串的特点,判断它是否有重复的字符,从而找到它的前缀与后缀,进而求出相应的Next数组,最终根据Next数组而进行KMP匹配。接下来,进入第二部分nextval数组。

请见连接(http://www.cnblogs.com/xiaoshanshan/p/4082011.html );

    

时间: 2024-10-05 21:45:47

KMP(课下总结)的相关文章

智慧解析第03课下:春秋故事 出逃

智慧解析第03课下:春秋故事 出逃,布布扣,bubuko.com

android从放弃到坚持放弃第二课(下)

续第二课( 下) 续第二课 下 活动的生命周期 返回栈 活动状态 活动的生存期 体验活动的生命周期 活动被回收怎么办 活动的启动模式 standard singleTop singleTask singleInstance 实践出真知 知晓当前是哪一个活动 随时随地退出程序 启动活动的最佳写法 问题 写app必须掌握活动的生命周期. [活动的生命周期] [返回栈] android每次启动的活动会覆盖在原活动之上,然后点击Back键会销毁最上层的活动.是使用Task来管理活动,一个任务就是一组存放

20155317 第八周课下作业(1)

20155317 第八周课下作业(1) 4.47: 冒泡排序实现: X86-64汇编: Y86-64汇编 4.48 实现冒泡排序,要求不使用跳转,且最多使用3次条件传送. X86-64汇编: y86汇编: 4.49 实现冒泡排序,要求不使用跳转,且最多使用1次条件传送. X86-64汇编: y86汇编

2017-2018-1 20155226 《信息安全系统设计基础》课下实践——实现mypwd

2017-2018-1 20155226 <信息安全系统设计基础>课下实践--实现mypwd 1 学习pwd命令 输入pwd命令 发现他是给出当前文件夹的绝对路径. 于是 man 1 pwd查看pwd详细 然后查看pwd实现需要的系统调用man -k; grep 在这发现了一个功能相同的内核函数getcwd 到这步就很简单了,先查看这个函数man getcwd 2 写出伪代码 char一个数组: 调用内核函数[getcwd()]获取当前目录的绝对路径并保存至数组中: if(返回的指针==NUL

# 2017-2018-1 20155302 课下实践IPC

2017-2018-1 20155302 课下实践IPC 共享内存 共享内存允许两个或多个进程共享一定的存储区,因为不需要拷贝数据,所以这是最快的一种IPC. 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间(这里的地址空间具体是哪个地方?)中.其他进程可以将同一段共享内存连接到自己的地址空间中.所有进程都可以访问共享内存中的地址,就好像它们是malloc分配的一样.如果一个进程向共享内存中写入了数据,所做的改动将立刻被

2017-2018-1 20155318 《信息安全系统设计基础》第十周课上测试及课下作业

2017-2018-1 20155318 <信息安全系统设计基础>第十周课上测试及课下作业 课上测试 解析:填充消除了冲突不命中,对于x和y数组,只有在引用第0个和第4个元素的时候发生不命中.因而命中率为75%. 解析:高速缓存容量为2048,高速缓存结构为(( 32 ),8,8,32) 解析:不同层之间是以块为大小传输单元在层与层之间复制,空缓存的不命中叫强制性不命中或冷不命中 解析:存储器层次结构的每一层都缓存来自较低一层的数据.缓存存储器是分块的,数据总是以块为基本单位在每一层之间传递,

2017-2018-1 20155330 《信息安全系统设计基础》课堂测试&amp;课下作业

2017-2018-1 20155330 <信息安全系统设计基础>课堂测试&课下作业 stat命令的实现-mysate 学习使用stat(1),并用C语言实现 提交学习stat(1)的截图 man -k ,grep -r的使用 伪代码 产品代码 mystate.c,提交码云链接 测试代码,mystat 与stat(1)对比,提交截图 STAT(1)学习 使用man 1 stat命令查看 使用man -k stat | grep 2查找相关函数 man 2 stat查看stat函数 产品

20155222卢梓杰 课下测试04补做

20155222卢梓杰 课下测试04补做 1.SEQ+对SEQ的改变有() A . PC的计算挪到取指阶段 B . PC的计算挪到访存阶段 C . 电路重定时 D . 插入流水线寄存器 E . 对信号进行重排和标号 F . 处理流水线冒险 正确答案: A C D E F 解析:在SEQ+中,创建状态寄存器来保存在一条指令执行过程中计算出来的信号.然后,当一个新的时钟周期开始时,这些信号值通过同样的逻辑来计算当前指令的PC. SEQ+中对状态元素的改变称为电路重定时,在SEQ+的各个阶段之间插入了

20155332 补交ch12课下作业

20155332 补交ch12课下作业 课下测试提交晚了,我课后补做了一遍,答对13题,答错3题. 试题内容如下所示: 课本内容 1.并发(Concurrency) 访问慢I/O设备:就像当应用程序等待I/O中的数据时内核会切换运行其他进程一样,我们的应用也可以用类似的方式,将I/O请求与其他工作重叠从而挖掘并发的潜能. 推迟工作而减少延迟:我们可以推迟一些耗时工作稍后执行,例如内存分配器不在free时整理碎片,而是将这些琐屑的工作推迟到一个稍后执行的独立"逻辑流"(logical f

2017-2018-1 20155214 《信息安全系统设计基础》 第9周课下测试-mypwd

2017-2018-1 20155214 <信息安全系统设计基础> 第9周课下测试-mypwd(深入版) 题目要求: 1 学习pwd命令 2 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 3 实现mypwd 4 测试mypwd 在第9周学习总结中,我直接调用了系统调用getcwd()输出当前绝对路径.这次我尝试用c语言实现它的功能. 函数原型 #include <unistd.h> char *getcwd(char *buf, size_t size);