KMP算法实现字符串的模式匹配的时间复杂度比朴素的模式匹配好很多,但是它时间效率的提高是有前提的,那就是:模式串的重复率很高,不然它的效率也不会凸显出来。在实际的应用中,KMP算法不算是使用率很高的一个算法,但是它的核心的那点东西却是使用率很高的,那就是next前缀数组的求解思路。在这次笔记中就单独摘出来,说一下前缀数组的求解。


1. next前缀数组的定义

不管做题还是推到算法,永远记住定义,这时最重要的东西。


2. next数组的暴力求解

这种方法的主要思路是:

为了求解nj的值,把的所有的前缀和后缀都找出来,然后从最大的开始匹配,直到找到合适的最长公共前缀后缀。如果没有,那么nj的值就是0。

前后缀的选取方式:

暴力算法就是在这里面不断的从最大的那个前缀和后缀逐一的匹配。

算法描述:

(1) 根据定义,初始化n[0] = –1。

(2) 从模式串的下标为1的位置,依次遍历整个模式串。对于每一个字符,当到达其下标j时,令k=j-1。

(3) 寻找它前面的字符串的最大公共前缀后缀,也就是判断的真假?

(4) 如果满足条件,令next[j]=k;如果不满足条件k--,继续执行(3)的步骤,直到k==0,然后令next[j]= 0。

代码实现:

#include <iostream>
#include <stdlib.h>
bool IsPatternMatch(char *p, int compareNum, int totalNum);
void ViolentGetNext(char *p, int *next); void main()
{
int next[];
char *str = "agctagcagctagctg";
ViolentGetNext(str, next); system("pause");
} void ViolentGetNext(char *p, int *next)
{
int pLen = strlen(p);
int k = ;
next[] = -; for(int j = ; j < pLen; j++)
{
k = j - ;
while(k > )
{
if(IsPatternMatch(p, k , j))
break;
else
k--;
}// while next[j] = k;
}// for
}
//param:copareNum代表了要比较的字节数
//param:totalNum代表了要比较的字节数
//上面的两个参数的作用就是定界前缀和后缀可能的范围
bool IsPatternMatch(char *p, int compareNum, int totalNum)
{
int i = ;
int j = totalNum - compareNum; for(; i < compareNum; i++, j++)
{
if(p[i] != p[j])
{
return false;
}
} return true;
}
具体的例子,假设字符串为ABCDABD
n[]的求解过程如下:
k=
ABCD≠BCDA,k=
ABC≠CDA,k=
AB≠DA,k=
A==A,n[]=k

3. next数组的递归求解

暴力求解每次在计算next[j]的时候都是独立的,而实际上求解next[j+1]是可以利用到next[0…j]的,这里的递归算法就是这样实现的。

设模式串为,现在已经计算出了next[0…j],如何计算next[j+1]?

利用前面求解的数值(这也是算法改进的地方,不让每个next元素都独立的计算),若已知next[j]=k,则对于模式串,肯定有这样的关系:

所以算法的描述可以是这样的:

(1) 如果k==-1(只有第一个字符的next值是-1),说明现在的位置是第二个位置,还不能算第二个它本身,所以next[j+1]=0,算法结束。

(2) 如果,理解这里的k是怎么从next[j]的值转换到了字符的下标值。则next[j+1]=k+1,算法结束。

提示:前面有分析过,求解next数组的过程的快捷方法就是不让他们独立的计算,还是继承前面计算好了的对称性。知道了next[j]的对称性,只需要在考察一下前缀和后缀的下一个字符是否相等就可以了。pk和pj就是之前最长前缀和后缀的下一个字符。

(3) 那么这个k’从哪里来的呢?看这个式子的两端就知道k’=next[k]。理解好上面的这个式子,就知道k‘是怎么来的了。

(4) 将k’赋值给k,转到步骤(1)。

代码实现:

//the recursion method to abtain the next array
//pLen is the length of the string
void RecursionGetNext(char *p, int pLen, int *next)
{ if(pLen == )
{
next[pLen - ] = -;
return;
} RecursionGetNext(p, pLen - , next); //pLen represents the number of the string
//pLen - 1 represents the index of the last character,that is the character that will be calculated in the next array.
//pLen - 1 - 1 represents the index of the sub-last character that has been calculated in the next array.
int k = next[pLen - ]; //k==-1 is a label showing that there is no prefix matching with postfix and the currently added character can not match neither.
//k==0 can only show that there is no prefix mathching with postfix,but pk may be match with pj
while(k >= )
{
if(p[pLen-] == p[k])
{
break;
}
else
{
k = next[k];
}
}//while next[pLen -] = k + ; }//RecursionGetNext()

4. next数组的递归展开求解

void GetNext(char *p, int *next)
{
int pLen = strlen(p);
int j = ;
int k = -;
next[] = -; while(j < pLen - )
{
//accroding to the depiction of the algorithm,the procedure can be programmed below:
//if(k == -1)
//{
// ++j;
// ++k;
// next[j] = k;
//}
//else if(p[j] == p[k])
//{
// ++j;
// ++k;
// next[j] = k;
//}
//but the fist two procedure can be reduced to one case: //p[j] == p[k] shows that we can inherite the feature of the string that matched alreay
//k==-1 shows two circumstance: 1.the beginning of the algorithm 2.there is no matched prefix and postfix and the last character is also defferent with the first one
if(k == - || p[j] == p[k])
{
++j;
++k;
next[j] = k;
}
else
{
k = next[k];
}
}//while
}

5. Reference Link

@wzhang1117的博客:https://www.zybuluo.com/wzhang1117/note/27431

KMP算法番外篇--求解next数组的更多相关文章

  1. 数据结构和算法 – 番外篇.时间测试类Timing

    public class Timing { //startingTime--用来存储正在测试的代码的开始时间. TimeSpan startingTime; //duration--用来存储正在测试的 ...

  2. 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV

    这次博客园的排版彻底残了..高清版请移步: https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程: 给深度学习入门者的Python快速教程 - 基础篇 给深度 ...

  3. 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  4. 可视化(番外篇)——SWT总结

    本篇主要介绍如何在SWT下构建一个应用,如何安装SWT Designer并破解已进行SWT的可视化编程,Display以及Shell为何物.有何用,SWT中的常用组件.面板容器以及事件模型等. 1.可 ...

  5. python自动化测试应用-番外篇--接口测试1

    篇1                 book-python-auto-test-番外篇--接口测试1 --lamecho辣么丑 1.1概要 大家好! 我是lamecho(辣么丑),至今<安卓a ...

  6. [uboot] (番外篇)uboot之fdt介绍

    http://blog.csdn.net/ooonebook/article/details/53206623 以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为 ...

  7. C++雾中风景番外篇:理解C++的复杂声明与声明解析

    在学习C系列语言的过程之中,理解C/C++的复杂声明一直是初学者很困扰的问题.笔者初学之时也深受困扰,对很多规则死记硬背.后续在阅读<C专家编程>之后,尝试在编译器的角度来理解C/C++的 ...

  8. [uboot] (番外篇)uboot之fdt介绍 (转)

    以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例 [uboot] uboot流程系列:[project X] tiny210(s5pv210)上电启动流程(B ...

  9. (八)羽夏看C语言——C番外篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...

随机推荐

  1. MFC渐入渐出框实现方式二

    类似360消息弹出框,实现方式一见http://blog.csdn.net/segen_jaa/article/details/7848598. 本文采用另外的API实现渐入渐出效果. 主要API:S ...

  2. 自定义标签体、MVC

    自定义标签 文件以tld结尾,放在webinfo中 标签名 引用类 标签体 继承SimpleTagSupport,复写doTag() getContext(); getjspBody()   invo ...

  3. Hat's Fibonacci(大数,好)

    Hat's Fibonacci Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  4. java--jsp+ssh+select动态结合数据和选择(解)

    在三层体系结构和jsp合并项目,如何实现select动态绑定数据和动态选择指定的行?让我们来看看下面的: 1.首先定义一个Bean分类.它用于实例select的结合数据中的每一个id和name: pu ...

  5. MD5和sha1加密算法

    在很多电子商务和社区应用中,我们都要存放很多的客户的资料,其中包括了很多的隐私信息和客户不愿被别人看到的信息,当然好有客户执行各种操作的密码,此时就需要对客户的信息进行加密再存储,目前有两种比较好的加 ...

  6. GridView合并多行列值

    )                    {                        gvr.Cells[cellNum].RowSpan = rowSpanNum;               ...

  7. myeclipse破解教程,对所有版本有效,完美支持32位和64位

    破解软件下载地址 执行Run.bat文件,按照以下步骤进行激活: 第一步:输入任意用户名 第二步:点击Systemid... 按钮,自动生成本机器的systemid. 第三步:点菜单Tools-> ...

  8. C++中的栈和队列

    使用标准库的栈和队列时,先包含相关的头文件 #include<stack> #include<queue> 定义栈如下: stack<int> stk; 定义队列如 ...

  9. 转载:js 创建对象、属性、方法

    1,自定义对象. 根据JS的对象扩展机制,用户可以自定义JS对象,这与Java语言有类似的地方. 与自定义对象相对应的是JS标准对象,例如Date.Array.Math等等. 2,原型(prototy ...

  10. scanf(),gets(),gechar()函数小结

    1. 使用scanf()函数从终端输入字符串时,刚开始输入的所有空格不计入当前字符串,以 space,enter,tab键结束当前字符串的输入:最后的space,enter,tab字符会留在输入缓冲区 ...