介绍

克努斯-莫里斯-普拉特算法Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个W的出现位置。此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符

此算法可以在O(n+m)时间数量级上完成串的模式匹配操作,其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i的指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的距离后,继续进行比较。

kmp的核心之处在于next数组,而为了方便理解,我先介绍KMP的思想

KMP匹配

当开始匹配时,如果匹配过程中产生“失配”时,指针i(原串的下标)不变,指针j(模式串的下标)退回到next[j] 所指示的位置上重新进行比较,并且当指针j退回至零时,指针i和指针j需同时加一。即主串的第i个字符和模式的第一个字符不等时,应从主串的第i+1个字符起重新进行匹配。

简单来说,就是两个串匹配,如果当前字符相等就比较两个字符串的下一个字符,如果当前匹配不相等时,就让j(待匹配串的下标)回到next[j] 的位置,因为我们已经知道next数组的作用是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的距离,如ababac与abac比较时i=4,j=4时不匹配,则利用next数组让j=2继续匹配而不用重新开始。(目前先不用管next数组的值时如何得到的,只要明白它的作用即可,下面回介绍)

所以我们可以写出kmp的代码

int KMP(char str[],char pat[])
{
int lenstr=strlen(str);
int lenpat=strlen(pat);
int i=1,j=1;
while(i<=lenstr)
{
if(j==0 || str[i]==pat[j]) //匹配成功继续往后匹配
++i,++j;
else
j=next[j]; //否则根据next数组继续匹配
if(j==lenpat) //说明匹配完成
return 1;
}
return 0;
}

接下来就是关键的求next数组了

next数组

首先,next数组取决于模式串本身而与相匹配的主串无关,我们可以对其递推得到。

网上讲next数组求解的博客一大堆,我就不那样从定义那一扯一大堆了,随便说一下如何推算的吧。

举个例子,如abacabc,首先,next[ 1 ]=0(下标从1开始),后面next[j]的值就看第j-1个字符是否与前面的匹配,如果匹配next[j]=next[j-1]+1,否则其他情况next[j]=1。这样说太笼统,我们看例子:

next[2]=1(第一个字符无法匹配,所以为1),

next[3]=1(第二个字符与第一个字符不相等),

next[4]=2(第三个字符与第一个字符相匹配,所以就等于next[3]+1),

next[5]=1(因为第4个字符与前面没有匹配的),

next[6]=2(同样第5个字符与第1个字符匹配),

next[7]=3(因为第6个字符b与第2个字符b匹配,同时由next[6]可知第5个字符与第1个字符同样匹配,即子串ab与ab匹配,故next[7]=next[6]+1)

下面给出几个例子,可以自己推导一下

abcdex
011111
abcabx
011123
ababaaaba
011234223
aaaaaaaab
012345678

如果理解了推导过程的话再回头看代码就好理解了,就算不理解也不要紧,先用着,过一段时间再去消化,下面是代码:

void getnext(char *pat)
{
int i=1,j=0;
int len=strlen(pat);
next[1]=0;
while(i<len)
{
if(j==0 || pat[i]==pat[j])
{
++i;
++j;
next[i]=j;
}
else
j=next[j];
}

优化

前面定义的next在某些情况下有缺陷,如模式串aaaab和主串aaabaaaab匹配时,仍有许多不必要的步骤,所以下面代码在这种情况做了优化:

void getnext(char *pat)
{
int i=1,j=0;
int len=strlen(pat);
next[1]=0;
while(i<len)
{
if(j==0 || pat[i]==pat[j])
{
++i;
++j;
if(pat[i]!=pat[j])
next[i]=j;
else
next[i]=next[j];
}
else
j=next[j];
}

模板

而下面是常用的模板,可以找到匹配下标与匹配次数,在与前面的略有些不同,其实就是next的值都减1罢了。

#include <iostream>
#include<cstring>
#include<cstdio>
using namespace std; char str[1000010],pat[1000010];//pat为模式串,str为主串
int Next[1000010]; //Next[x]下标x表示匹配失败处字符下标
//模式串pat的前缀与x位置的后缀的最大匹配字符个数-1
void GetNext(char *pat)
{
int LenPat = strlen(pat);
int i = 0,j = -1;
Next[0] = -1;
while(i < LenPat)
{
if(j == -1 || pat[i] == pat[j])
{
i++,j++;
Next[i] = j;
}
else
j = Next[j];
}
} int KMP()//返回模式串pat在str中第一次出现的位置
{
int LenStr = strlen(str);
int LenPat = strlen(pat);
GetNext(pat);
int i = 0,j = 0;
int ans = 0;//计算模式串在主串匹配次数
while(i < LenStr)
{
if(j == -1 || str[i] == pat[j])
i++,j++;
else
j = Next[j];
if(j == LenPat)
{
//ans++; ans存放匹配次数,去掉return,最后返回ans
return i - LenPat + 1;
}
}
return -1;//没找到匹配位置
//return ans;//返回匹配次数。
}
int main()
{
scanf("%s%s",str,pat);
int i=KMP();
printf("%d\n",i);
return 0;
}

KMP算法(推导方法及模板)的更多相关文章

  1. KMP算法,匹配字符串模板(返回下标)

    //KMP算法,匹配字符串模板 void getNext(int[] next, String t) { int n = next.length; for (int i = 1, j = 0; i & ...

  2. 什么是KMP算法?KMP算法推导

    花了大概3天时间,了解,理解,推理KMP算法,这里做一次总结!希望能给看到的人带来帮助!! 1.什么是KMP算法? 在主串Str中查找模式串Pattern的方法中,有一种方式叫KMP算法 KMP算法是 ...

  3. KMP算法自我理解 和 模板

    字符串   abcd abc abcd abc 匹配串   cdabcd 匹配串的 next  0 0 0 0 1 2: 开始匹配 abcd abc abcd abc cd abc d a,d 匹配失 ...

  4. 解读KMP算法

    前后断断续续搞了5个月,每次都以为自己懂了, 但是要写的时候都不知从何下手,然后又是各种找博客,看帖子,所以这次试着用自己的语言写一个博客. 首先,KMP算法就是从一个模板字符串(S) 中匹配目标字符 ...

  5. KMP算法与传统字符串寻找算法

    原理:KMP算法是一种模板匹配算法,它首先对模板进行便利,对于模板中与模板首字符一样和首字符进行标志-1,对于模板匹配中出现不匹配的若是第一轮检查标志为0,若不是第一轮检查标志为该元素与标志为-1的距 ...

  6. Luogu 3375 【模板】KMP字符串匹配(KMP算法)

    Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...

  7. [模板]KMP算法

    昨天晚上一直在调KMP(模板传送门),因为先学了hash[关于hash的内容会在随后进行更(gu)新(gu)]于是想从1开始读...结果写出来之后一直死循环,最后我还是改回从0读入字符串了. [预先定 ...

  8. KMP算法(——模板习题与总结)

    KMP算法是一种改进的模式匹配算法,相比于朴素的模式匹配算法效率更高.下面讲解KMP算法的基本思想与实现. 先来看一下朴素模式匹配算法的基本思想与实现. 朴素模式匹配算法的基本思想是匹配过程中如果该位 ...

  9. KMP算法模板&&扩展

    很不错的学习链接:https://blog.csdn.net/v_july_v/article/details/7041827 具体思路就看上面的链接就行了,这里只放几个常用的模板 问题描述: 给出字 ...

随机推荐

  1. c#--早绑定晚绑定

    原文地址 早绑定early binding: 在编译的时候就已经却确定了将来程序运行基类或者派生类的哪个方法. 在编译代码的时候根据引用类型就决定了运行该引用类型中定义的方法.即基类方法. 这种方式运 ...

  2. 公众号和app和web都是客户端,都可以对接一个后台

    1.公众号和app和web都是客户端,都可以对接一个后台 2.域名中包含端口号吗?:不包括,不包括 3.目前在IIS服务器上搭建了一个网站,域名也申请了,可是80端口不能使用,可以使用8000,每次访 ...

  3. MRv1到MRv2

    概述 引入YARN作为通用资源调度平台后.Hadoop得以支持多种计算框架,如MapReduce.Spark.Storm等. MRv1是Hadoop1中的MapReduce,MRv2是Hadoop2中 ...

  4. 解决TortoiseGit下载代码每次要输入用户名、密码

    解决办法: 方案1: 右键>ortoiseGit → Settings → Git → Credential 设置为 wincred - this repository only 或者 winc ...

  5. HTML5的data-*自己定义属性

    HTML5添加了一项新功能是自己定义数据属性.也就是data-*自己定义属性.在HTML5中我们能够使用以data-为前缀来设置我们须要的自己定义属性,来进行一些数据的存放.当然高级浏览器下可通过脚本 ...

  6. Hdu-6242 2017CCPC-哈尔滨站 M.Geometry Problem 计算几何 随机

    题面 题意:给你n个点,让你找到一个圆,输出圆心,和半径,使得有超过一半的点刚好在圆上.n<=1e5,题目保证了有解 题解:刚开始看着很不可做的样子,但是多想想,三点确定一个圆,三点啊! 现在有 ...

  7. JDBC数据库中表更新

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sq ...

  8. 在量化金融中15个最流行的Python数据分析库

    Python是当今应用最广泛的编程语言之一,以其效率和代码可读性著称.作为一个科学数据的编程语言,Python介于R和java之间,前者主要集中在数据分析和可视化,而后者主要应用于大型应用.这种灵活性 ...

  9. Prism学习(1)---前期准备

    本文摘取自Gene's Blog的博客园文章,版权归Gene's Blog,仅供个人学习参考.转载请标明原作者Gene's Blog. 在学习Prism框架之前,我预先写了一个非常简单的计算器解决方案 ...

  10. C#泛型类的用途和说明

    class Program    {        public class Test<T, S>        {           //泛型类的类型参数可用于类成员          ...