KMP算法(推导方法及模板)
介绍
克努斯-莫里斯-普拉特算法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算法(推导方法及模板)的更多相关文章
- KMP算法,匹配字符串模板(返回下标)
//KMP算法,匹配字符串模板 void getNext(int[] next, String t) { int n = next.length; for (int i = 1, j = 0; i & ...
- 什么是KMP算法?KMP算法推导
花了大概3天时间,了解,理解,推理KMP算法,这里做一次总结!希望能给看到的人带来帮助!! 1.什么是KMP算法? 在主串Str中查找模式串Pattern的方法中,有一种方式叫KMP算法 KMP算法是 ...
- KMP算法自我理解 和 模板
字符串 abcd abc abcd abc 匹配串 cdabcd 匹配串的 next 0 0 0 0 1 2: 开始匹配 abcd abc abcd abc cd abc d a,d 匹配失 ...
- 解读KMP算法
前后断断续续搞了5个月,每次都以为自己懂了, 但是要写的时候都不知从何下手,然后又是各种找博客,看帖子,所以这次试着用自己的语言写一个博客. 首先,KMP算法就是从一个模板字符串(S) 中匹配目标字符 ...
- KMP算法与传统字符串寻找算法
原理:KMP算法是一种模板匹配算法,它首先对模板进行便利,对于模板中与模板首字符一样和首字符进行标志-1,对于模板匹配中出现不匹配的若是第一轮检查标志为0,若不是第一轮检查标志为该元素与标志为-1的距 ...
- Luogu 3375 【模板】KMP字符串匹配(KMP算法)
Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...
- [模板]KMP算法
昨天晚上一直在调KMP(模板传送门),因为先学了hash[关于hash的内容会在随后进行更(gu)新(gu)]于是想从1开始读...结果写出来之后一直死循环,最后我还是改回从0读入字符串了. [预先定 ...
- KMP算法(——模板习题与总结)
KMP算法是一种改进的模式匹配算法,相比于朴素的模式匹配算法效率更高.下面讲解KMP算法的基本思想与实现. 先来看一下朴素模式匹配算法的基本思想与实现. 朴素模式匹配算法的基本思想是匹配过程中如果该位 ...
- KMP算法模板&&扩展
很不错的学习链接:https://blog.csdn.net/v_july_v/article/details/7041827 具体思路就看上面的链接就行了,这里只放几个常用的模板 问题描述: 给出字 ...
随机推荐
- jqm文件上传,上传图片,jqm的表单操作,jqm的ajax的使用,jqm文件操作大全,文件操作demo
近期在论坛中看到.在使用html5中上传图片或文件,出现各种问题. 这一方面,我也一直没有做过,今天就抽出了一点时间来学习一下.如今的演示样例已经ok了,我就给大家分享一下,希望对大家有帮助. 好吧. ...
- ReflectionSugar 通用反射类
http://www.cnblogs.com/sunkaixuan/p/4635710.html
- Dragon Ball--hdoj
Dragon Ball Problem Description Five hundred years later, the number of dragon balls will increase u ...
- Node.js:目录
ylbtech-Node.js:目录 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 1. http://www.runoob.com/nodejs/nodejs ...
- 排序系列 之 希尔排序算法 —— Java实现
基本思想: 希尔排序的实质就是分组插入排序,又称缩小增量法. 将整个无序序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本 ...
- 5个对话框和FileStream:文件流
1.private void button1_Click(object sender, EventArgs e) { colorDialog1.ShowDialog();//显示颜色选择器 panel ...
- angular4搭建博客(一)
本文长期更新,未经运行,严禁转载. 博客(制作中) http://101.200.58.228/ Github https://github.com/Teloi/TEIndex 框架选择 Angula ...
- go结构,结构嵌套,接口,指针的测试和结论
package main import ( "fmt" ) //T是M1接受者,不是实现M2接受者 //*T是M1接受者,也是M2的接受者 //所以T对象不可以赋值给接口对象.*T ...
- 开源作品-ThinkPHP在线分析工具(单文件绿色版)-TPLogAnalysis_PHP_1_0
TPLogAnalysis_PHP_1_0 前言:项目开发基于ThinkPHP框架,但是在调试程序的时候,没有一款日志可视化分析工具.在网络也找不到任何相关的TP日志分析工具.求人不如求己,于是决定抽 ...
- PCL:解决PCL和OpenCV冲突的方法
不是PCL的问题,而是OpenCV的问题. (1):先包含PCL库,再包含OpenCV库: (2):把里面的UCHAR冲突全部换掉! 如果你有闲情逸致,用正则表达式 慢慢替换去吧! (3):或者把F ...