KMP算法证明及实现

KMP算法

一、普通的字符串匹配

平时我们在写普通的字符串匹配算法的时候,是拿着要匹配的串去匹配被匹配的串,字符逐个比较,当发现字符失配时,被匹配的字符串的指针要回到前一次开始匹配的指针的下一个位置。这里我们称要去匹配的字符串为模式串P,被匹配的字符串为主串S,即我们拿模式串P去匹配主串S,看看P是否是S的子串。

例如:主串S是“abcabdsfabcdfrt”,模式串P是“abcd”,开始匹配时,可以看到,S和P的字符串0、1、2位置的字符是相同的,到3位置时出现不匹配,这是根据我们以往的方法,我们将主串的指针回到刚开始匹配的位置的下一个位置,即回到主串的1位置,即字符b,再拿着模式串的0位置去匹配。以此类推,每次出现失配时,主串的指针都回溯到初始匹配位置的下一个位置,模式串的指针都回到模式串的0位置。

二、为何要用KMP算法

当我们的模式串P中,出现了连续相同的字符串时,例如P=“abcabx”,我们拿这个P和主串S=“abcabqqeeabcabxxxaxxaa”去匹配时。开始匹配可以看到,主串S的0、1、2、3、4位置和模式串的0、1、2、3、4位置的字符都是相同的,接着两个串的指针都下移,到了S的5位置的字符就和P的5位置的字符就出现吧不匹配,根据以往经验我们会将S的指针回溯到1位置接着和P的0位置进行匹配。这是我们发现,我们的P的0-1和3-4位置的字符串是一样的,而且S的0-4已经和P的0-4匹配是相同的,所以主串S的3、4位置和模式串P的0、1位置是相同的。

如果用主串的1、2位置与模式串从头匹配,则都是失配的,主串S的3、4位置和模式串P的0、1位置是相同的,所以我们就可以不回溯主串的指针,让模式串的2位置直接与主串的当前位置进行匹配即可。

三、KMP算法的数学推导

根据上面的情况,我们推广到一般情况。我们用i表示指向主串的指针,j表示指向模式串的指针,当主串的第i个字符和模式串的第j个字符失配时,主串中的第i个字符(指针i不回溯)应该与模式串中的哪个位置的字符再比较呢,假设我们的主串已经和模式串匹配比较到模式串的第k个字符了,那么模式串的前k-1个字符一定和主串的第i-k+1到i-1个字符是相同的,即

P1 P2 ……Pk-1=Si-k+1 Si-K+2…… Si-1

而已经得到的部分匹配结果是

Pj-k+1 Pj-k+2 ……Pj-1=Si-k+1 Si-K+2…… Si-1

由以上两式推导得出

P1 P2 ……Pk-1= Pj-k+1 Pj-k+2 ……Pj-1

反之,若模式串中存在满足上式的两个子串,则当匹配过程中,主串中的第i个字符和模式串中的第j个字符比较不相等时,仅需要将模式串向右滑动至模式串中的第k个字符和主串中第i个字符对齐,(此时,由于模式串中前k-1个字符和第i-k到i-1位置的字符都是对应相同的,所以模式串中前k-1个字符的子串P1 P2 ……Pk-1必定与主串中第i个字符之前长度为k-1的子串Si-k+1 Si-K+2…… Si-1相等)接着匹配从模式串的第k个字符与主串的第i个字符比较起继续进行。

四、求每一个位置对应的k(即next数组)

我们用next数组来存取模式串中每一个位置对应的k值,即next[j]表示当模式串中第j个字符和模式串中相应的字符失配时,在模式串中重新和主串中该字符进行比较的字符的位置。

根据三中的数学推导,得到了next函数的定义(假设字符串起始位置是1)

当j=1时,next[j]=0;

当j!=1时,next[j]=max(k|1<k<j且P1 P2 ……Pk-1= Pj-k+1 Pj-k+2 ……Pj-1)此集合不    为空时。若此集合为空,next[j]=1。

例,求模式串“abaabcac”的next数组


j


1


2


3


4


5


6


7


8


模式串


a


b


a


a


b


c


a


c


Next[j]


0


1


1


2


2


3


1


2

四、代码实现

public class KMP {

/*

* Kmp函数实现寻找模式串str2在主串str1中的位置,返回值即为str2在str1中的位置,

* 如果str2不是str1的子串则返回-1.

*/

private static int Kmp(String str1,String str2){

//第一步 求next[j]数组

char[] strKey = str2.toCharArray();

int[] next = new int[strKey.length];

// 初始条件

int j1 = 0;

int k = -1;

next[0] =-1;

// 根据已知的前j位推测第j+1位

while (j1 < strKey.length - 1)

{

if (k == -1 || strKey[j1] == strKey[k])

{

next[++j1] = ++k;

}

else

{

k = next[k];

}

}

//打印一下我们的next数组

System.out.print("next[]的值 ");

for (int i = 0; i < next.length; i++) {

System.out.print(next[i]+1+" ");

}

System.out.println();

//第二步 根据求得的next数组进行字符串匹配

int j=0;//j指向模式串str2,i指向主串str1.

for (int i = 0; i < str1.length(); i++) {

if(j==str2.length()) return i-j;

if(str1.charAt(i)==str2.charAt(j)) j++;

else j=next[j]+1;

}

return -1;

}

//测试数据

public static void main(String[] args) {

String str1="12345abaabcac2356";

String str2="abaabcac";

int a=Kmp(str1,str2);

System.out.println(a);

}

}

时间: 2024-10-25 04:27:00

KMP算法证明及实现的相关文章

(转载)KMP算法详解(写的很好)

原文地址:http://www.cnblogs.com/yjiyjige/p/3263858.html KMP算法应该是每一本<数据结构>书都会讲的,算是知名度最高的算法之一了,但很可惜,我大二那年压根就没看懂过~~~ 之后也在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么一回事,但总感觉有些地方自己还是没有完全懂明白.这两天花了点时间总结一下,有点小体会,我希望可以通过我自己的语言来把这个算法的一些细节梳理清楚,也算是考验一下自己有真正理解这个算法. 什么是KMP算法: K

KMP算法 - 求最小覆盖子串

KMP与最小覆盖子串 最小覆盖子串:对于某个字符串s,它的最小覆盖子串指的是长度最小的子串p,p满足通过自身的多次连接得到q,最后能够使s成为q的子串. 比如: 对于s="abcab",它的最小覆盖子串p="abc",因为p通过在它后面再接上一个p(即重叠0个字符),可以得到q="abcabc",此时s是q的子串. 对于s="ababab",它的最小覆盖子串为p="ab". 根据KMP算法的next数组的定

KMP算法解析(转自图灵社区)

KMP算法是一个很精妙的字符串算法,个人认为这个算法十分符合编程美学:十分简洁,而又极难理解.笔者算法学的很烂,所以接触到这个算法的时候也是一头雾水,去网上看各种帖子,发现写着各种KMP算法详解的转载帖子上面基本都会附上一句:“我也看的头晕”——这种诉苦声一片的错觉仿佛人生苦旅中找到知音,让我几乎放弃了这个算法的理解,准备把它直接记在脑海里了事. 但是后来在背了忘忘了背的反复过程中发现一个真理:任何对于算法的直接记忆都是徒劳无功的,基本上忘得比记的要快.后来看到刘未鹏先生的这篇文章:知其所以然(

KMP算法详解 --- 彻头彻尾理解KMP算法

[经典算法]——KMP,深入讲解next数组的求解 前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k:但是问题在于如何求出这个最大前后缀长度呢?我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破,后来翻看算法导论,32章 字符串匹配虽然讲到了对前后缀计算的正确性,但是大量的推理证明不大好理解,没有与程序结合起来讲.今天我在这里讲一讲我的一些理解,希望大家多多指教,如果有不清楚的或错误的请给我留言. 1.kmp算法的原理: 本部分内容转自:http://w

KMP算法深度解析

[原文参考] http://www.ics.uci.edu/~eppstein/161/960227.html 摘要:KMP算法是字符串匹配的经典算法,由于其O(m+n)的时间复杂度,至今仍被广泛应用.大道至简,KMP算法非常简洁,然而,其内部却蕴含着玄妙的理论,以至许多人知其然而不知其所以然.本文旨在解开KMP算法的内部玄妙所在,希望能够有助于学习与理解. 1.KMP算法    一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此称之为KMP算

[转]KMP算法

KMP字符串模式匹配详解 分类: 算法 2013-02-12 19:26 2380人阅读 评论(0) 收藏 举报 个人觉得这篇文章是网上的介绍有关KMP算法更让人容易理解的文章了,确实说得很“详 细”,耐心地把它看完肯定会有所收获的--,另外有关模式函数值next[i]确实有很多版本啊,在另外一些面向对象的算法描述书中也有失效函数 f(j)的说法,其实是一个意思,即next[j]=f(j-1)+1,不过还是next[j]这种表示法好理解啊: KMP字符串模式匹配详解 KMP字符串模式匹配通俗点说

KMP算法的正确性证明及一个小优化

直接把作业帖上来是不是有点不太公道呀... 无所谓啦反正各位看着开心就行 KMP算法 对于模式串$P$,建立其前缀函数$ N$ ,其中$N [q] $ 表示在$P$中,以$q$位置为结束的可以匹配到前缀的最长后缀的长度(也可以理解为那个前缀的结束位置),在匹配中,若$P[i]$与$S[j]$失配,则令$i=N [i-1] +1$ ,否则$i=i+1,j=j+1$ 现考虑如何构造$N$ ,设当前以计算出$N[1..i-1]$ ,则令$k=N[i-1]$ ,若 $P[k+1]=P[i]$,则令$N[

【初识】KMP算法入门

举个例子 模式串S:a s d a s d a s d f a s d 匹配串T:a s d a s d f 如果使用朴素匹配算法—— 1 2 3 4 5 6 7 8 9 a s d a s d a s d f a s d a s d a s d f 1 2 3 4 5 6 7 此时,匹配到了S7和T7了,S7为a而T7为f,不匹配那么朴素的匹配算法会这么做—— 1 2 3 4 5 6 7 8 9 a s d a s d a s d f a s d a s d a s d f 1 2 3 4 5

KMP算法思路总结

KMP算法一开始学起来要被绕晕,但事实上,只要掌握其中逻辑思路,还是很好学的. 我们设主串为S,子串为a 现在第一部分两者匹配,也就是说,S串的打钩部分与a串打钩部分是完全一样的,但是,S[i]与a[k]是不相同的.如果按照常规思路,我们只会把a串往后移一个(朴素算法的过程也可以这么理解),但这样时间上会浪费不少,如何优化呢? 假设,在第一部分匹配区中,a串存在这样一个子串(它既在a的头部,并且又在a的尾部,且x的长度尽可能大...事实上是要求最大化),我们把这个子串叫做x,那么,我们可以看到,