KMP初步

Posted qgmzbry

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KMP初步相关的知识,希望对你有一定的参考价值。

KMP算法专门用于处理字符串匹配问题。

开始学习的时候觉得很有道理,但是一些细节总觉得有些模糊,所以一直觉得懵懵懂懂。今天思考了一下,总结一下,希望对大家也有帮助。

朴素的字符串匹配算法就是一个一个字符挨个去试,但是当匹配串长度比较长的时候复杂度显然会爆炸。

为了解决这个问题,很厉害的三个人想出来了这个很厉害的算法。

 

 

核心思想是假如比较到第j+1个字符的时候匹配失败,不是像朴素算法一样从头开始比较,而是利用匹配串自身的特性直接跳转到匹配串头部,这个头部和尾部相同。这是比较容易理解的,显然这样做是正确的,但是为什么能够保证不会漏掉可能存在的其他匹配串呢?

现在我们用反证法来看这个问题:即假在串s1中查找串s2,如果比较到si的第i个元素,s2的第j个元素,发现s1[i+1]!=s2[j+1],此时我们可以直接跳转到s2[p[j]]处继续匹配s1和s2,p[j]是指s2[1..p[j]]和s2[i-p[j]+1..i]相同的最大p[j],现在证明这样做不会漏掉前面(s1[i-j+1..i-p[j]+1]这些部分是没有进行比较的)可能存在的其他串。

假如在那个区域存在与s2匹配的串,因为长度问题,这个串结束的部分一定超过i,而这个串在i前面的部分和s2[1..j]相同,但是它比最大的p[j]还长,这样的串是不可能存在的,同时也说明了为什么KMP需要最大的p[j]。

 

现在基本思想已经证明正确,所要做的就是思考如何实现这个算法了。问题主要有两个:如何得到p[j]和如何实现匹配算法。

为了方便理解,这里先介绍如何实现匹配算法:

技术图片
 1 void kmp()
 2 {
 3     j=0;//匹配串的指针 
 4     for(int i=0;i<n;i++)    //之所以是小于n是因为会判断i+1处的值
 5     {
 6         while(j>0 && a[i+1]!=b[j+1]) j=p[j];//j>0说明在j前面存在匹配,存在匹配而出现不匹配就要将j指针前移到和末尾相同而且下一位支持匹配的地方
 7         if(b[j+1]==a[i+1]) ++j;
 8         if(j==m) 
 9         {
10             printf("%d",i+1-m+1);//直接输出匹配位置
11             j=p[j];    //继续寻找(可重叠)匹配 
12             //j=0;    寻找不重叠匹配 
13         }
14     }
15 }
View Code

这个过程的复杂度为O(n),具体为什么需要进行摊还分析(我也不是太懂。。。)。

然后我们就要考虑如何得到p[j]了。

我们不难发现(不难个鬼哦),在求p[j]的过程其实就是在前j-1个字符中查找第j个位置组成的子串的过程,因此得到p[j]的过程其实和上述过程类似不同之处在于每次查找结束后都要更新那个位置的p[j]的值。

技术图片
 1 void pre()
 2 {
 3     p[1]=0;
 4     j=0;    //j指i所匹配的位置 
 5     for(int i=1;i<m;i++)
 6     {
 7         while(j>0 && b[j+1]!=b[i+1]) j=p[j];
 8         if(b[i+1]==b[j+1]) ++j;
 9         p[i+1]=j;
10     }
11 }
View Code

大概就是这样,嗯~

以上是关于KMP初步的主要内容,如果未能解决你的问题,请参考以下文章

KMP初步

KMP算法初步理解

AC自动机初步

BZOJ4641基因改造 KMP

php初步

POJ-2752(KMP算法+前缀数组的应用)