KMP算法-如何理解

合集下载

KMP算法(原创)PPT教学课件

KMP算法(原创)PPT教学课件

? 这时如何求next[j+1]呢
2020/12/10
11
转化法
式1的结论可这样描述:何时的k使得
pk=pj,就用此时的k代入式1。
而现在的k是pk!=pj,因此必须要换成另外 一个“k”,并设它为k2,以使得pk2=pj。
问题又出来了: k2如何得来?
如图: 0 j-k+1
j
P’
2/10
2
(2) Brute-Force算法的实现
int String::Find Substr(const String& t, int start)const {
int i = start, j = 0, v; while(i < size && j < t.size) {
if(str[i] == t.str[j]) {i++;j++;} else {i = i-j+1;j = 0;} } if(j >= t.size-1) v = i-t.size+1; else v = -1; return v; }
由(1)(2)两式便可得:
‘p0p1……pk-1’= ‘pj-kpj-k+1……pj-1’ (3) (3)式的结论可如下描述:
在模式p中,前k个字符与第j个字符之前 的k个字符相同。
2020/12/10
7
设next[j]表示:当模式中第j个字符与正 文中相应字符“失配”时,在模式中重 新和正文中该字符进行比较的字符的位 置。
利用next数组进行模式匹配示例:
2020/12/10
9
如何预先求得next数组值
首先要明确一点: next数组的求值只与 模式p有关,而与具体的正文s无关。

实验04:串应用KMP算法PPT课件

实验04:串应用KMP算法PPT课件

在生物信息学中的应用
在生物信息学中,KMP算法被广泛应用于基因序列的比对和拼接,以及蛋白质序列 的匹配和比对。
通过构建基因序列或蛋白质序列的索引表,KMP算法可以在O(n+m)的时间复杂度 内完成序列的比对和拼接,提高了比对和拼接的准确性和效率。
KMP算法在生物信息学中的应用有助于深入了解基因和蛋白质的结构和功能,为生 物医学研究和疾病诊断提供了有力支持。
06 实验总结与展望
KMP算法的优缺点
优点
高效:KMP算法在匹配失败时能跳过 尽可能多的字符,减少比较次数,从
而提高匹配效率。
适用范围广:KMP算法适用于各种模 式串匹配问题,不受模式串长度的限 制。
缺点
计算量大:KMP算法需要计算和存储 部分匹配表,对于较长的模式串,计 算量较大。
不适合处理大量数据:KMP算法在处 理大量数据时可能会占用较多内存, 导致性能下降。
匹配失败的处理
当模式串中的某个字符与主串中的对应字符不匹配时,模式串向右 滑动,与主串的下一个字符重新对齐,继续比较
next[ j]表示当模式串中第j个字符与主 串中的对应字符不匹配时,模式串需 要向右滑动的位置。
next数组的构建
next数组的作用
在匹配过程中,通过next数组可以快 速确定模式串需要滑动到哪个位置, 从而提高了匹配效率。
通过已知的next值,递推计算出next 数组中其他位置的值。
KMP算法的时间复杂度
01
02
03
04
时间复杂度分析
KMP算法的时间复杂度取决 于模式串在主串中出现的次数 以及每次匹配所花费的时间。
最佳情况
当模式串在主串中连续出现时 ,KMP算法的时间复杂度为

kmp算法next计算方法

kmp算法next计算方法

kmp算法next计算方法KMP算法是一种用于字符串匹配的经典算法,它的核心在于通过预处理模式串,得到一个next数组,然后利用这个数组在匹配过程中进行快速跳转,从而提高匹配效率。

本文将介绍KMP算法中next数组的计算方法。

在KMP算法中,next数组的含义是指在模式串中,以每个字符结尾的子串中,有多大长度的相同前缀后缀。

这个信息非常有用,因为当遇到不匹配的字符时,我们可以利用next数组中的信息,快速地将模式串向后移动,而不是从头开始逐个字符地比较。

接下来我们来看一下next数组的计算方法。

假设模式串为P,长度为m,我们要计算出next数组的值。

首先,我们定义next[0]=-1,next[1]=0,这两个是特殊情况。

然后,我们从第二个字符开始,依次计算next[i]的值。

具体的计算方法如下:1. 如果P[j]等于P[next[j]],则next[j+1]=next[j]+1;2. 如果P[j]不等于P[next[j]],则需要继续向前寻找,直到找到一个满足P[j]等于P[next[j]]的位置,或者找到0为止。

这样,我们就可以得到整个next数组的值。

这个过程实际上是在模式串中寻找相同的前缀后缀,然后记录下它们的长度。

这样,在匹配过程中,当遇到不匹配的字符时,我们就可以根据next数组中的值,快速地将模式串向后移动,从而提高匹配效率。

需要注意的是,由于next数组的计算是基于模式串本身的特性,因此对于不同的模式串,其next数组的值也是不同的。

这就要求我们在实际使用KMP算法时,需要提前计算好next数组,并将其保存下来,以备匹配过程中使用。

总结一下,KMP算法中next数组的计算方法是一个非常重要的步骤,它直接影响到算法的匹配效率。

通过提前计算好next数组,并在匹配过程中利用它,我们可以大大提高字符串匹配的效率,从而更高效地解决实际问题。

希望本文对KMP算法中next数组的计算方法有所帮助,如果有任何疑问或者建议,欢迎留言讨论。

kmp算法概念

kmp算法概念

kmp算法概念KMP算法概念KMP算法是一种字符串匹配算法,它的全称是Knuth-Morris-Pratt 算法。

该算法通过预处理模式串,使得在匹配过程中避免重复比较已经比较过的字符,从而提高了匹配效率。

一、基本思想KMP算法的基本思想是:当模式串与文本串不匹配时,不需要回溯到文本串中已经比较过的位置重新开始匹配,而是利用已知信息跳过这些位置继续匹配。

这个已知信息就是模式串自身的特点。

二、next数组1.定义next数组是KMP算法中最核心的概念之一。

它表示在模式串中当前字符之前的子串中,有多大长度的相同前缀后缀。

2.求解方法通过观察模式串可以发现,在每个位置上出现了相同前缀和后缀。

例如,在模式串“ABCDABD”中,第一个字符“A”没有任何前缀和后缀;第二个字符“B”的前缀为空,后缀为“A”;第三个字符“C”的前缀为“AB”,后缀为“B”;第四个字符“D”的前缀为“ABC”,后缀为“AB”;第五个字符“A”的前缀为“ABCD”,后缀为“ABC”;第六个字符“B”的前缀为“ABCDA”,后缀为“ABCD”;第七个字符“D”的前缀为“ABCDAB”,后缀为“ABCDA”。

根据上述观察结果,可以得到一个求解next数组的方法:(1)next[0]=-1,next[1]=0。

(2)对于i=2,3,...,m-1,求解next[i]。

①如果p[j]=p[next[j]],则next[i]=next[j]+1。

②如果p[j]≠p[next[j]],则令j=next[j],继续比较p[i]和p[j]。

③重复执行步骤①和步骤②,直到找到满足条件的j或者j=-1。

(3)通过上述方法求解出所有的next值。

三、匹配过程在匹配过程中,文本串从左往右依次与模式串进行比较。

如果当前字符匹配成功,那么继续比较下一个字符;否则利用已知信息跳过一些位置继续进行匹配。

具体地:(1)如果当前字符匹配成功,则i和j都加1。

(2)如果当前字符匹配失败,则令j=next[j]。

KMP算法(改进的模式匹配算法)——next函数

KMP算法(改进的模式匹配算法)——next函数

KMP算法(改进的模式匹配算法)——next函数KMP算法简介KMP算法是在基础的模式匹配算法的基础上进⾏改进得到的算法,改进之处在于:每当匹配过程中出现相⽐较的字符不相等时,不需要回退主串的字符位置指针,⽽是利⽤已经得到的部分匹配结果将模式串向右“滑动”尽可能远的距离,再继续进⾏⽐较。

在KMP算法中,依据模式串的next函数值实现字串的滑动,本随笔介绍next函数值如何求解。

next[ j ]求解将 j-1 对应的串与next[ j-1 ]对应的串进⾏⽐较,若相等,则next[ j ]=next[ j-1 ]+1;若不相等,则将 j-1 对应的串与next[ next[ j-1 ]]对应的串进⾏⽐较,⼀直重复直到相等,若都不相等则为其他情况题1在字符串的KMP模式匹配算法中,需先求解模式串的函数值,期定义如下式所⽰,j表⽰模式串中字符的序号(从1开始)。

若模式串p 为“abaac”,则其next函数值为()。

解:j=1,由式⼦得出next[1]=0;j=2,由式⼦可知1<k<2,不存在k,所以为其他情况即next[2]=1;j=3,j-1=2 对应的串为b,next[2]=1,对应的串为a,b≠a,那么将与next[next[2]]=0对应的串进⾏⽐较,0没有对应的串,所以为其他情况,也即next[3]=1;j=4,j-1=3 对应的串为a,next[3]=1,对应的串为a,a=a,所以next[4]=next[3]+1=2;j=5,j-1=4 对应的串为a,next[4]=2,对应的串为b,a≠b,那么将与next[next[4]]=1对应的串进⾏⽐较,1对应的串为a,a=a,所以next[5]=next[2]+1=2;综上,next函数值为 01122。

题2在字符串的KMP模式匹配算法中,需先求解模式串的函数值,期定义如下式所⽰,j表⽰模式串中字符的序号(从1开始)。

若模式串p为“tttfttt”,则其next函数值为()。

KMP算法(超容易理解的next数组求法)

KMP算法(超容易理解的next数组求法)

KMP算法(超容易理解的next数组求法)我想,既然知道KMP算法了,⾃然对于其具体如何运作也是有⼀定的了解的,我也没必要再⼤费⼝⾆讲废话,⼤家⼀定查过很多资料,但是其实也都看的⼀知半解,好像⾃⼰懂了但是⼜没有理解透,特别是next数组的求法感觉很蛋疼,那么我就来给⼤家解决⼀下这个next数组的问题吧,如果对KMP算法没有概念的同学请先去查清楚再过来看看。

先讲⼀下我们后续讲解的⼀些规范,我们next数组、以及模式串都是从数组的下标1开始储存的,注意不是0!(这样操作⽅便⼀点)下⾯列出⼀个next数组:模式串A B A B A B Bj1234567next[j]0112345很多⼈可能对这个next数组的理解就是不正确的,我们来好好解释⼀下:第⼀个元素的next值,即next[1],默认是0,这是规定的,0则表⽰从头开始⽐较那么很多⼈会疑惑,那上⾯这个例⼦中,j=2的时候如果不匹配,不也是应该从头开始⽐较吗?那么为什么next[2]不是0⽽是1呢?好,你可能会机智地说,next[1]=0是你规定死的,⽽你的模式串⼜是从下标为1的地⽅保存起来的,当然next[2] = 1啦!我们知道KMP算法的匹配是通过next数组慢慢往前回复(如果匹配失败的话)的⼀个过程,那么经过next数组返回后,我们究竟是从哪⾥开始⽐较呢?是从返回的那⼀个开始,还是它的前⼀个,亦或是后⼀个?这就涉及对next数组的真正的理解了,我们来好好理⼀理,这个next数组到底是怎么⼀回事!所谓next数组的返回,其实是前⾯有⼀部分是重叠的,才能这样回退。

⽤⽂字可能很难描述,我们直接拿上⾯的来举例。

j = 4的时候,这个B前⾯的A和第⼀个A是相同的,因此next[4]=2,这个2是怎么来的呢?这个2不是因为j=2的那个B,⽽是因为j=1的那个A,因为j=1的A和j=3的A相匹配了,因此next[4] = 1 + 1,这才等于的2。

等号右边第⼀个1是j=1的1,第⼆个1是往右边⾛⼀位的1。

KMP算法详解

KMP算法详解

KMP算法详解KMP 算法详解KMP 算法是⼀个⼗分⾼效的字符串查找算法,⽬的是在⼀个字符串 s 中,查询 s 是否包含⼦字符串 p,若包含,则返回 p 在 s 中起点的下标。

KMP 算法全称为 Knuth-Morris-Pratt 算法,由 Knuth 和 Pratt 在1974年构思,同年 Morris 也独⽴地设计出该算法,最终由三⼈于1977年联合发表。

举⼀个简单的例⼦,在字符串 s = ababcabababca 中查找⼦字符串 p = abababca,如果暴⼒查找,我们会遍历 s 中的每⼀个字符,若 s[i] = p[0],则向后查询p.length() 位是否都相等。

这种朴素的暴⼒的算法复杂度为O(m×n),其中m和n分别是 p 和 s 的长度。

KMP 算法可以⽅便地简化这⼀查询的时间复杂度,达到O(m+n)。

1. PMT 序列PMT 序列是 KMP 算法的核⼼,即 Partial Match Table(部分匹配表)。

举个例⼦:char a b a b a b c aindex01234567PMT00123401PMT 的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。

PMT[0] = 0: 字符串 a 既没有前缀,也没有后缀;PMT[1] = 0: 字符串 ab 前缀集合为 {a},后缀集合为 {b},没有交集;PMT[2] = 1: 字符串 aba 前缀集合为 {a, ab},后缀集合为 {ba, a},交集为 {a},交集元素的最长长度为1;PMT[3] = 2: 字符串 abab 前缀集合为 {a, ab, aba},后缀集合为 {bab, ab, b},交集为 {ab},交集元素的最长长度为2;…… 以此类推。

2. 算法主体现在我们已经知道了 PMT 序列的含义,那么假设在 PMT 序列已经给定的情况下,如何加速字符串匹配算法?tar 存储 s 的下标,从 0 开始,若 tar > s.length() - 1,代表匹配失败;pos 存储 p 的下标,从 0 开始,若 s[tar] != p[pos],则 pos ⾛到下⼀个可能匹配的位置。

数据结构字符串对称的判断算法

数据结构字符串对称的判断算法

一、介绍在计算机科学中,数据结构是指在计算机中组织和存储数据的一种特殊方式。

而字符串对称的判断算法则是在数据结构中的一个重要应用,它用来判断一个字符串是否是对称的,即该字符串从左到右和从右到左读是一样的。

这是一个很常见的算法问题,在很多面试和编程挑战中经常会遇到。

本文将介绍一些常见的字符串对称判断算法,以帮助读者更好地理解和掌握这一算法。

二、暴力法暴力法是最简单的一种字符串对称判断算法。

它的思路是遍历字符串,同时比较对应位置上的字符是否相同。

具体步骤如下:1. 从字符串的两端分别设置两个指针,分别指向字符串的首尾字符。

2. 比较两个指针所指向的字符是否相同,如果相同则继续比较下一对字符,如果不同则说明该字符串不是对称的,算法结束。

3. 重复上述步骤直到两个指针相遇,如果过程中没有出现不同的情况,则说明该字符串是对称的。

暴力法的时间复杂度为O(n),其中n为字符串的长度。

但这种算法并不高效,因为它需要遍历整个字符串并逐个比较字符,所以在处理较长的字符串时效率并不高。

三、栈的应用栈是一种后进先出的数据结构,可以用来判断字符串是否对称。

具体步骤如下:1. 遍历字符串,将字符串的每个字符依次入栈。

2. 将栈中的字符逐个出栈,同时与字符串的对应位置上的字符进行比较,如果出现不同的情况则说明该字符串不是对称的,算法结束。

3. 如果整个遍历过程中没有出现不同的情况,且栈中所有字符都已经出栈,说明该字符串是对称的。

栈的应用在判断字符串对称时的时间复杂度也为O(n),但相较于暴力法,使用栈可以在一定程度上提高效率。

四、递归算法递归算法也可以用来判断字符串是否对称。

其思路是将字符串分割成两部分,分别比较这两部分的对应字符是否相同。

具体步骤如下:1. 将字符串分割成左右两部分,如果字符串长度为奇数,则左侧字符串比右侧多一个字符。

2. 逐个比较左右两部分对应位置上的字符,如果出现不同的情况则说明该字符串不对称,算法结束。

深度剖析KMP

深度剖析KMP

深度剖析KMP,让你认识真正的NextKMP算法,想必大家都不陌生,它是求串匹配问题的一个经典算法(当然如果你要理解成放电影的KMP,请退出本页面直接登录各大电影网站,谢谢),我想很多人对它的理解仅限于此,知道KMP能经过预处理然后实现O(N*M)的效率,比brute force(暴力算法)更优秀等等,其实KMP算法中的Next函数,功能十分强大,其能力绝对不仅仅限于模式串匹配,它并不是KMP的附属品,其实它还有更多不为人知的神秘功能^_^先来看一个Next函数的典型应用,也就是模式串匹配,这个相信大家都很熟悉了:POJ 3461 Oulipo——很典型的模式串匹配问题,求模式串在目标串中出现的次数。

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define MAX 1000001char t[MAX];char s[MAX];int next[MAX];inline void calnext(char s[],int next[]){int i;int j;int len=strlen(s);next[0]=-1;j=-1;for(i=1;i<len;i++){while(j>=0&&s[i]!=s[j+1])j=next[j];if(s[j+1]==s[i])//上一个循环可能因为j=-1而不做,此时不能知道s[i]与s[j+1]的关系。

故此需要此条件。

j++;next[i]=j;}}int KMP(char t[],char s[]){int ans=0;int lent=strlen(t);int lens=strlen(s);if(lent<lens)return 0;int i,j;j=-1;for(i=0;i<lent;i++){while(j>=0&&s[j+1]!=t[i])j=next[j];if(s[j+1]==t[i])j++;if(j==lens-1){ans++;j=next[j];}}return ans;}int main(){int testcase;scanf("%d",&testcase);int i;for(i=1;i<=testcase;i++){scanf("%s%s",s,t);calnext(s,next);printf("%d\n",KMP(t,s));}return 0;}—————————————————————————————————————————————POJ 2406 Power Strings这道题就比较有意思了,乍看之下,怎么看貌似都与KMP无关,呵呵,这就是因为你没有深入理解Next 的含义;我首先来解释下这道题的题意,给你一个长度为n的字符串,首先我们找到这样一个字符串,这个字符串满足长度为n的字符串是由这个字符串重复叠加得到并且这个字符串的长度要最小.,然后输出重复的次数。

数据结构教学中KMP算法解析

数据结构教学中KMP算法解析

数据结构教学中KMP算法解析摘要:模式匹配是字符串的基本运算之一,也是数据结构教学中的难点之一。

分析了模式匹配KMP算法以及算法中next函数的含义,给出了next函数的两种实现方法,有助于在教学实践中帮助学生更好地理解该算法。

关键词:数据结构;模式匹配;KMP算法0引言模式匹配(Patten Matching)是许多计算机应用领域的基础问题,在数据结构中模式匹配是字符串的基本运算之一。

字符串模式匹配指的是,找出特定的模式串在一个较长的字符串中出现的位置。

有两个字符串S和T,字符串S称为目标串,字符串T称为模式串,要求找出模式T在S中的首次出现的位置。

一旦模式T在目标S中找到,就称发生一次匹配。

有些应用可能会要求找出所有的匹配位置<sup>[1]</sup>。

例如,目标串S= 'Shanghai',模式串T= 'gha',则匹配结果为4。

模式匹配的典型算法包括朴素匹配算法、KMP算法和BM算法等,其中KMP算法是效率较高且经典的模式匹配算法之一<sup>[2]</sup>。

在数据结构教学中,由于KMP算法较难理解,课堂讲授往往很难取得好的效果。

本文通过对传统的朴素匹配算法与KMP算法的比较,分析next函数的含义以及实现方法,来帮助理解KMP算法。

1朴素匹配算法在朴素匹配算法中,S和T分别为目标串和模式串,变量i和j 为两个静态指针,分别表示S和T中当前正待比较的字符位置。

算法的基本思想是:第1趟匹配:从S的第1个字符(序号为0)起和T的第一个字符比较之,如果相等,则继续逐个比较后续字符(i++;j++),否则开始下一趟匹配。

新的一趟匹配:i的初值为上一趟的初值+1 ,j的初值为1,如果比较结果相等,则继续逐个比较后续字符,否则开始下一趟匹配。

依次类推,直至某一趟匹配中,T的每个字符依次和S中的一个连续的字符序列相等,则称匹配成功,否则称匹配不成功。

KMP算法-易懂版

KMP算法-易懂版

KMP算法-易懂版⼀:定义 Knuth-Morris-Pratt 字符串查找算法,简称为 KMP算法,常⽤于快速查找⼀个母串S中是否包含⼦串(模式串)P,以及P出现的位置。

由于简单的暴⼒匹配中,每次遇到不匹配的位置时都要回溯到母串上⼀次的起点 i +1的位置上再次从⼦串的开头进⾏匹配,效率极其低下,故⽽KMP算法应运⽽⽣,减少回溯过程中不必要的匹配部分,加快查找速度。

⼆:kmp算法求解步骤描述 若当前不匹配的位置发⽣在母串位置 i,⼦串位置 j 上,则:1. 寻找⼦串位置 j 之前元素的最长且相等的前后缀,即最长公共前后缀。

记录这个长度。

2. 根据这个长度求 next 数组3. 若 j != 0, 则根据next [j] 中的值,将⼦串向右移动,也就是将公共前缀移到公共后缀的位置上,(代码表⽰为:j=next [j],注意 i 不变),即对位置 j 进⾏了更新,后续⼦串直接从更新后的 j 位置和母串 i 位置进⾏⽐较。

4. 若 j == 0,则 i+1,⼦串从j位置开始和母串 i+1 位置开始⽐较。

综上,KMP的next 数组相当于告诉我们:当⼦串中的某个字符跟母串中的某个字符匹配失败时,⼦串下⼀步应该跳到哪个位置开始和母串当前失配位置进⾏⽐较。

所以kmp算法可以简单解释为:如⼦串在j 处的字符跟母串在i 处的字符失配时,下⼀步就⽤⼦串next [j] 处的字符继续跟⽂本串 i 处的字符匹配,相当于⼦串⼀次向右移动 j - next[j] 位,跳过了⼤量不必要的匹配位置(OK,简单理解完毕之后,下⾯就是求解KMP的关键步骤,Let’s go! ) 三:kmp算法关键步骤之⼀,求最长的公共前后缀! 箭头表⽰当前匹配失败的位置,也就是当前的 j 位置。

⽩框表⽰最长公共前后缀AB!此时长度为2! 再来⼀个,此时最长公共前后缀为ABA!长度为3!四:kmp算法关键步骤之⼆,求next[ ] 数组 由步骤⼀,我们可以得到⼦串每个位置前⾯元素的最长共同前后缀,注意⼦串第⼀个位置是没有前后缀的,所以长度为0! 例:⼦串ABCDABD的最长公共前后缀可表⽰如下。

kmp next算法

kmp next算法

kmp next算法KMP算法(Knuth-Morris-Pratt Algorithm)是一种字符串匹配算法,它的核心思想是利用已经得到的匹配结果,尽量减少字符的比较次数,提高匹配效率。

本文将详细介绍KMP算法的原理、实现方法以及应用场景。

一、KMP算法的原理KMP算法的核心是构建next数组,用于指导匹配过程中的回溯操作。

next数组的定义是:对于模式串中的每个字符,记录它前面的子串中相同前缀和后缀的最大长度。

next数组的长度等于模式串的长度。

具体来说,KMP算法的匹配过程如下:1. 初始化主串指针i和模式串指针j为0。

2. 逐个比较主串和模式串对应位置的字符:- 若主串和模式串的字符相等,i和j同时后移一位。

- 若主串和模式串的字符不相等,根据next数组的值,将模式串指针j回溯到合适的位置,继续匹配。

二、KMP算法的实现KMP算法的实现可以分为两个步骤:构建next数组和利用next数组进行匹配。

1. 构建next数组:- 首先,next[0]赋值为-1,next[1]赋值为0。

- 然后,从第2个位置开始依次计算next[i],根据前一个位置的next值和模式串的字符进行判断:- 若前一个位置的next值为-1或模式串的字符与前一个位置的字符相等,则next[i] = next[i-1] + 1。

- 若前一个位置的next值不为-1且模式串的字符与前一个位置的字符不相等,则通过next数组的回溯操作,将模式串指针j回溯到合适的位置,继续判断。

2. 利用next数组进行匹配:- 在匹配过程中,主串指针i和模式串指针j会同时后移:- 若主串和模式串的字符相等,i和j同时后移一位。

- 若主串和模式串的字符不相等,则根据next数组的值,将模式串指针j回溯到合适的位置,继续匹配。

三、KMP算法的应用场景KMP算法在字符串匹配中有广泛的应用,特别是在大规模文本中的模式匹配问题上具有明显的优势。

以下是KMP算法的几个应用场景:1. 子串匹配:判断一个字符串是否是另一个字符串的子串。

字符串匹配kmp算法

字符串匹配kmp算法

字符串匹配kmp算法字符串匹配是计算机科学中的一个基本问题,它涉及在一个文本串中寻找一个模式串的出现位置。

其中,KMP算法是一种更加高效的算法,它不需要回溯匹配过的字符,在匹配失败的时候,根据已经匹配的字符和模式串前缀的匹配关系直接跳跃到下一次匹配的起点。

下面,我将详细介绍KMP算法原理及其实现。

1. KMP算法原理KMP算法的核心思想是:当模式串中的某个字符与文本串中的某个字符不相同时,根据已经匹配的字符和模式串前缀的匹配关系,跳过已经比较过的字符,从未匹配的字符开始重新匹配。

这个过程可以通过计算模式串的前缀函数(即next数组)来实现。

具体地,假设现在文本串为T,模式串为P,它们的长度分别为n和m。

当对于文本串T的第i个字符和模式串P的第j个字符(i和j都是从0开始计数的)进行匹配时:如果T[i]和P[j]相同,则i和j都加1,继续比较下一个字符;如果T[i]和P[j]不同,则j回溯到next[j](next[j]是P[0]到P[j-1]的一个子串中的最长的既是自身的前缀又是后缀的子串的长度),而i不会回溯,继续和P[next[j]]比较。

如果匹配成功,则返回i-j作为P在T中的起始位置;如果匹配失败,则继续执行上述过程,直到文本串T被遍历完或匹配成功为止。

2. KMP算法步骤(1)计算模式串的前缀函数next[j]。

next[j]表示P[0]到P[j-1]的一个子串中的最长的既是自身的前缀又是后缀的子串的长度。

具体计算方式如下:先令next[0]=-1,k=-1(其中k表示相等前缀的长度,初始化为-1),j=0。

从j=1向后遍历整个模式串P:如果k=-1或者P[j]=P[k],则next[j+1]=k+1,k=j,j+1;否则,令k=next[k],再次执行步骤2。

(2)使用next数组进行匹配。

从文本串T的第0个字符开始,从模式串P的第0个字符开始匹配,如果匹配失败,根据next数组进行回溯。

KMP算法

KMP算法

KMP算法KMP算法是一种用于字符串匹配的快速算法,全称为Knuth-Morris-Pratt算法,是由Donald Knuth、Vaughan Pratt和James Morris在1977年共同提出的。

该算法的核心思想是通过利用已经匹配过的部分来避免不必要的字符比较,从而提高匹配效率。

1.暴力匹配算法在介绍KMP算法之前,我们先来了解一下暴力匹配算法。

暴力匹配算法,又称为朴素匹配算法,是最基本的匹配方法,它的思想就是从主串的第一个字符开始,逐个比较主串和模式串的字符,直到匹配成功或者主串和模式串的所有字符都比较完毕。

具体算法如下:```暴力匹配(主串S,模式串P):i=0j=0n = length(S)m = length(P)while i < n and j < m:if S[i] == P[j]: // 匹配成功,继续比较下一个字符i++else: // 匹配失败,模式串向后移动一位i=i-j+1j=0if j == m: // 匹配成功return i - jelse: // 匹配失败return -1```暴力匹配算法的时间复杂度为O(n*m),其中n和m分别为主串和模式串的长度。

2.KMP算法的思想KMP算法的关键在于构建一个部分匹配表,通过这个表来确定模式串在匹配失败时应该移动的位置。

部分匹配表的定义如下:对于模式串P的前缀子串P[0:i],如果存在一个真前缀等于真后缀,则称其长度为i的真前缀的真后缀长度为部分匹配值。

假设有一个模式串P,我们定义一个部分匹配表next,其中next[i]表示在P[i]之前的子串(不包括P[i])中,有多大长度的相同前缀后缀。

例如,P="ABCDABD",则next[7]=2,因为在P[7]之前的子串中,"ABD"是长度为3的前缀,也是长度为3的后缀。

构建部分匹配表的算法如下:构建部分匹配表(P):m = length(P)next = [0] * m // 初始化部分匹配表j=0k=-1next[0] = -1while j < m - 1:if k == -1 or P[j] == P[k]: // P[j]表示后缀的单个字符,P[k]表示前缀的单个字符j++k++next[j] = kelse:k = next[k]```构建部分匹配表的时间复杂度为O(m),其中m为模式串的长度。

kmp百度百科

kmp百度百科

kmp算法[编辑本段]kmp算法-概述一种改进的字符串匹配算法,由 D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。

[编辑本段]kmp算法-学习介绍完全掌握KMP算法思想学过数据结构的人,都对KMP算法印象颇深。

尤其是新手,更是难以理解其涵义,搞得一头雾水。

今天我们就来面对它,不将它彻底搞懂,誓不罢休。

如今,大伙基本上都用严蔚敏老师的书,那我就以此来讲解KMP 算法。

(小弟正在备战考研,为了节省时间,很多课本上的话我都在此省略了,以后一定补上。

)严老的《数据结构》79页讲了基本的匹配方法,这是基础。

先把这个搞懂了。

80页在讲KMP算法的开始先举了个例子,让我们对KMP的基本思想有了最初的认识。

目的在于指出“由此,在整个匹配的过程中,i指针没有回溯,”。

我们继续往下看:现在讨论一般情况。

假设主串:s: ‘s(1) s(2) s(3) ……s(n)’; 模式串:p: ‘p(1) p(2) p(3)…..p(m)’把课本上的这一段看完后,继续现在我们假设主串第i个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i个字符与模式串的第k(k<j)个字符继续比较此时,s(i)≠p(j), 有主串:S(1)……s(i-j+1)……s(i-1) s(i) ………….|| (相配) || ≠(失配)匹配串:P(1) ……. p(j-1) p(j)由此,我们得到关系式‘p(1) p(2) p(3)…..p(j-1)’= ’s(i-j+1)……s(i-1)’由于s(i)≠p(j),接下来s(i)将与p(k)继续比较,则模式串中的前(k-1)个字符的子串必须满足下列关系式,并且不可能存在k’>k 满足下列关系式:(k<j),‘p(1) p(2) p(3)…..p(k-1)’= ’s(i-k+1)s(i-k+2)……s(i-1)’即:主串:S(1)……s(i-k +1) s(i-k +2) ……s(i-1) s(i) ………….|| (相配) || || ?(有待比较)匹配串:P(1) p(2) ……p(k-1) p(k)现在我们把前面总结的关系综合一下有:S(1)…s(i-j +1)…s(i-k +1) s(i-k +2) ……s(i-1) s(i) ……|| (相配) || || || ≠(失配)P(1) ……p(j-k+1) p(j-k+2) ….... p(j-1) p(j)|| (相配) || || ?(有待比较)P(1) p(2) ……. p(k-1) p(k)由上,我们得到关系:‘p(1) p(2) p(3)…..p(k-1)’= ’s(j-k+1)s(j-k+2)……s(j-1)’接下来看“反之,若模式串中存在满足式(4-4)。

KMP算法

KMP算法

从头到尾彻底理解KMP作者:July时间:最初写于2011年12月,2014年7月21日晚10点全部删除重写成此文,随后的半个多月不断反复改进。

后收录于新书《编程之法:面试和算法心得》第4.4节中。

1. 引言本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP,思路混乱导致写也写得混乱。

所以一直想找机会重新写下KMP,但苦于一直以来对KMP的理解始终不够,故才迟迟没有修改本文。

然近期因开了个算法班,班上专门讲解数据结构、面试、算法,才再次仔细回顾了这个KMP,在综合了一些网友的理解、以及算法班的两位讲师朋友曹博、邹博的理解之后,写了9张PPT,发在微博上。

随后,一不做二不休,索性将PPT 上的内容整理到了本文之中(后来文章越写越完整,所含内容早已不再是九张PPT 那样简单了)。

KMP本身不复杂,但网上绝大部分的文章(包括本文的2011年版本)把它讲混乱了。

下面,咱们从暴力匹配算法讲起,随后阐述KMP的流程步骤、next 数组的简单求解递推原理代码求解,接着基于next 数组匹配,谈到有限状态自动机,next 数组的优化,KMP的时间复杂度分析,最后简要介绍两个KMP的扩展算法。

全文力图给你一个最为完整最为清晰的KMP,希望更多的人不再被KMP折磨或纠缠,不再被一些混乱的文章所混乱。

有何疑问,欢迎随时留言评论,thanks。

2. 暴力匹配算法假设现在我们面临这样一个问题:有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢?如果用暴力匹配的思路,并假设现在文本串S匹配到 i 位置,模式串P 匹配到 j 位置,则有:如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。

相当于每次匹配失败时,i 回溯,j 被置为0。

理清楚了暴力匹配算法的流程及内在的逻辑,咱们可以写出暴力匹配的代码,如下:[cpp]view plaincopyprint?1.int ViolentMatch(char* s, char* p)2.{3.int sLen = strlen(s);4.int pLen = strlen(p);5.6.int i = 0;7.int j = 0;8.while (i < sLen && j < pLen)9.{10. if (s[i] == p[j])11. {12. //①如果当前字符匹配成功(即S[i] == P[j]),则i++,j++13. i++;14. j++;15. }16. else17. {18. //②如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 019. i = i - j + 1;20. j = 0;21. }22. }23. //匹配成功,返回模式串p在文本串s中的位置,否则返回-124. if (j == pLen)25. return i - j;26. else27. return -1;28.}举个例子,如果给定文本串S“BBC ABCDAB ABCDABCDABDE”,和模式串P“ABCDABD”,现在要拿模式串P去跟文本串S匹配,整个过程如下所示:1.S[0]为B,P[0]为A,不匹配,执行第②条指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,S[1]跟P[0]匹配,相当于模式串要往右移动一位(i=1,j=0)2. S[1]跟P[0]还是不匹配,继续执行第②条指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,S[2]跟P[0]匹配(i=2,j=0),从而模式串不断的向右移动一位(不断的执行“令i = i - (j - 1),j = 0”,i 从2变到4,j一直为0)3. 直到S[4]跟P[0]匹配成功(i=4,j=0),此时按照上面的暴力匹配算法的思路,转而执行第①条指令:“如果当前字符匹配成功(即S[i] == P[j]),则i++,j++”,可得S[i]为S[5],P[j]为P[1],即接下来S[5]跟P[1]匹配(i=5,j=1)4. S[5]跟P[1]匹配成功,继续执行第①条指令:“如果当前字符匹配成功(即S[i] == P[j]),则i++,j++”,得到S[6]跟P[2]匹配(i=6,j=2),如此进行下去5. 直到S[10]为空格字符,P[6]为字符D(i=10,j=6),因为不匹配,重新执行第②条指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,相当于S[5]跟P[0]匹配(i=5,j=0)6. 至此,我们可以看到,如果按照暴力匹配算法的思路,尽管之前文本串和模式串已经分别匹配到了S[9]、P[5],但因为S[10]跟P[6]不匹配,所以文本串回溯到S[5],模式串回溯到P[0],从而让S[5]跟P[0]匹配。

kmp算法详解bymatrix67

kmp算法详解bymatrix67

KMP算法详解如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段。

我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法。

KMP算法是拿来处理字符串匹配的。

换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。

比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串。

你可以委婉地问你的MM:“假如你要向你喜欢的人表白的话,我的名字是你的告白语中的子串吗?”解决这类问题,通常我们的方法是枚举从A串的什么位置起开始与B匹配,然后验证是否匹配。

假如A串长度为n,B串长度为m,那么这种方法的复杂度是O (mn)的。

虽然很多时候复杂度达不到mn(验证时只看头一两个字母就发现不匹配了),但我们有许多“最坏情况”,比如,A= "aaaaaaaaaaaaaaaaaaaaaaaaaab",B="aaaaaaaab"。

我们将介绍的是一种最坏情况下O(n)的算法(这里假设m<=n),即传说中的KMP算法。

之所以叫做KMP,是因为这个算法是由Knuth、Morris、Pratt三个提出来的,取了这三个人的名字的头一个字母。

这时,或许你突然明白了A VL 树为什么叫A VL,或者Bellman-Ford为什么中间是一杠不是一个点。

有时一个东西有七八个人研究过,那怎么命名呢?通常这个东西干脆就不用人名字命名了,免得发生争议,比如“3x+1问题”。

扯远了。

个人认为KMP是最没有必要讲的东西,因为这个东西网上能找到很多资料。

但网上的讲法基本上都涉及到“移动(shift)”、“Next函数”等概念,这非常容易产生误解(至少一年半前我看这些资料学习KMP时就没搞清楚)。

在这里,我换一种方法来解释KMP算法。

假如,A="abababaababacb",B="ababacb",我们来看看KMP 是怎么工作的。

kmp算法c语言代码

kmp算法c语言代码

kmp算法c语言代码KMP算法C语言代码KMP算法是一种字符串匹配算法,它的核心思想是利用已知信息来避免无效的比较,从而提高匹配效率。

KMP算法的实现需要用到一个next数组,它记录了模式串中每个位置之前的最长公共前后缀的长度。

下面是KMP算法的C语言代码实现:```cvoid getNext(char* pattern, int* next) {int i = 0, j = -1;next[0] = -1;while (pattern[i]) {if (j == -1 || pattern[i] == pattern[j]) {i++;j++;next[i] = j;} else {j = next[j];}}}int kmp(char* text, char* pattern) {int i = 0, j = 0;int text_len = strlen(text);int pattern_len = strlen(pattern);int* next = (int*)malloc(sizeof(int) * pattern_len); getNext(pattern, next);while (i < text_len && j < pattern_len) {if (j == -1 || text[i] == pattern[j]) {i++;j++;} else {j = next[j];}}free(next);if (j == pattern_len) {return i - j;} else {return -1;}}```在上面的代码中,getNext函数用来计算next数组,kmp函数用来进行字符串匹配。

在getNext函数中,i表示当前位置,j表示最长公共前后缀的长度。

如果当前位置和最长公共前后缀的下一个位置相等,那么最长公共前后缀的长度加1;否则,j跳到next[j]的位置。

KMP算法(推导方法及模板)

KMP算法(推导方法及模板)

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;elsej=next[j]; //否则根据next数组继续匹配if(j==lenpat) //说明匹配完成return 1;}return 0;}接下来就是关键的求next数组了next数组⾸先,next数组取决于模式串本⾝⽽与相匹配的主串⽆关,我们可以对其递推得到。

关于KMP算法和BM算法的理解

关于KMP算法和BM算法的理解

关于KMP算法和BM算法的理解自己之前没有接触到模式匹配的问题。

对于所谓的字符串匹配也只是简单的蛮力匹配。

上周刚看了《多模式匹配算法及破件实现》这篇文章。

文章中主要讲的是多模式匹配算法: AC算法和WM算法以及这两个算法的改进。

AC算法是基于KMP算法的,WM算法是基于BM算法的。

只有充分理解了单模式匹配的KMP算法和BM算法才能理解多模式匹配算法, 最后才能对模式匹配有一个深入的理解。

卜面主要说卜•自己对KMP算法和BM算法的理解。

KMP算法和BM算法,它们分别是前缀匹配和后缀匹配的经典算法。

所谓前缀匹配是指: 模式串和母串的比较从左到右,模式串的移动也是从左到右:所谓后缀匹配是指:模式串和母串的的比较从右到左,模式串的移动从左到右。

看得岀来前缀I兀配和后缀匹配的区别就仅仅在于比较的顺序不同。

KMP算法KMP算法,是由Knuth, Morris, Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,足一个非常优秀的模式匹配算法。

KMP算法对于朴素匹配算法的改进是引入了一个跳转表next[]。

但next[]刚接触时比较难于理解。

next跳转表,在进行模式匹配,实现模式串向后移动的过程中,发挥了更要作用。

这个表看似神奇,实际从原理上讲并不复杂,对于模式串而言,其前缀字符串,有町能也是模式串中的非前缀子串,这个问题我称之为前缀包含问题。

以模式串abcabcacab为例,其前缀abca,止好也是模式串的一个子串abc(abca)cab,所以当目标串与模式串执行匹配的过程中,如果直到第8个字符才匹配失败,同时也意味着目标串当前字符Z前的4个字符, 与模式串的前4个字符是相同的,所以当模式串向后移动的时候,可以直接将模式串的第5 个字符与当前字符对齐,执行比较,这样就实现了模式串一次性向前跳跃多个字符。

这里只是一个模式串移动的思想。

如何以较小的代价计算KMP算法中所用到的跳转表next,是算法的核心问题。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

对KMP算法的理解
整理者——戴红伟
字符匹配算法的现实意义:随着互联网的日渐庞大,信息也是越来越多,如何在海量的信息中快速查找自己所要的信息是网络搜索研究的热点所在,在这其中,字符串匹配算法起着非常重要的作用,一个高效的字符串匹配算法,可以极大的提高搜索的效率和质量。

(请同时参照课本P53~54相关内容)
1.要理解next[j]=k 中,k的含意;
(1)BF算法
假设有字符串
S=S1S2......S N
P=P1P2......P M
其中(M<N),要求返回P在S中出现的位置。

所以算法要点如下:
假设从S中i点开始扫描(也就是从S[i]开始,此时用一个变量k=i),顺序比较S[i]和P[1],S[i+1]和P[2]。

这样,当P进行到第j个字符也就是P[j]的时候,发现S[i+j-1]和P[j]不匹配,那么需要把S的指针i回朔到S起始的下一个字符也就是k+1继续比较。

如图:
以上就是模式匹配的基本算法,这种算法对于大多数的匹配来说基本是O(N+M)的时间复杂度。

但是对于如下的字符串:
S=0000000000000000000000000000001
P=000001
这种字符串来说,每次匹配失败都是在P到最后一个字符也就是j=M的时候,此时S 的指针又必须回朔,导致大量的匹配。

此时的时间复杂度是O(N*M)。

所以基本算法的时间复杂度是O(N*M)。

当然了,对于大多数的匹配是不会有这么高的时间复杂度的,所以这种算法现在也在广泛使用,因为简单。

(2)KMP算法
为了解决上述的问题,KMP算法被发现。

KMP算法的思想如下。

匹配过程中,出现不匹配时,S的指针不进行回朔(原地不动),将P尽可能地向后移动一定的距离,再进行匹配。

如图:
(该图引用自互联网)
从上图中我们看到,当S移动到i,P到j的时候失配。

这时候i不回朔,而只是将P 向前移动尽可能的距离,继续比较。

假设,P向右移动一定距离后,第k个字符P[k]和S[i]进行比较。

此时如上图,当P[j]和S[i]失配后,i不动,将P前移到K,让P[k]和S[i]继续匹配。

现在的关键是K的值是多少?
通过上图,我们发现,因为黄色部分表示已经匹配了的结果(因为是到了S[i]和P[j]的时候才失配,所以S i-j+1S i-j+2…S i-1 = P1P2…P j-1,见黄色的部分)。

所以有:
1、S i-k+1S i-k+2…S i-1 = P j-k+1P j-k+2…P j-1。

所以当P前移到K时,有:
2、S i-k+1S i-k+2…S i-1 = P1P2…P k-1。

通过1,2有
P j-k+1P j-k+2…P j-1 = P1P2…P k-1。

呵呵,此时我们的任务就是求这个k值了。

参考:/2008-09/122068902261358.html
2.求出k 值
按照课本的求法就可以处理。

课本是已知前j个元素的“前缀函数值”,如何求的j+1个元素的前缀函数值。

这里有一个思路要发生转变的地方,把一个模式串分成两个部分,因为我们要找k使得P j-k+1P j-k+2…P j-1= P1P2…P k-1,而这本身就是一个模式匹配问题,所以把模式串的前边部分的子串当作“新的模式串”,这样就很容易理解为什么当t k!=t j时,t1…t next[k]-1 = t j-(next[k]-1)…t j-1了。

因为这时候t k匹配失败,需要进一步移动模式子串,所以移动的位置就是next[k]。

相关文档
最新文档