模式匹配之KMP

来源:互联网 发布:广播电视网络宽带 编辑:程序博客网 时间:2024/05/16 04:15

注:模式串指我们要找是不是出现的串,主串指我们匹配的对象。

简介:

一种由Knuth(D.E.Knuth)Morris(J.H.Morris)Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法。

大致原理:

其实就是利用已经匹配过的字符,也就是在中间的每一个位置,就断开了。利用前面的部分去设置,下次寻找的点。

普通的就是这个失败,就返回刚开始的位置的下一个。继续寻找。不断的,不断的。。。

KMP的话,就是看你找过且对的上的部分的规律,通过这个规律,我们判断下一次找的点的位置,从模式串里面的那个位置开始。

比如下面的一个例子:

00000001001     


1不匹配的时候,我们是返回第二个位置这样呢?(当然KMP也是这样的,但是下面的我们指只要匹配不成功,就下面一个位置继续,重新开始的那种普通匹配。)

0000001 001   

以上的理由,为什么呢?这个为什么就是KMP的学习之处?还是这样?(下面的我们指直接将模式串移过来。进行判断。)

他通过判断制定一个next的规则,进行模式串下一次的位置判断点。就可以直接判断。从而实现O(n+m)的时间复杂度。

 

重点:每一个next的值,都是表示前看,后看的最长公共长度。  

 

就几个例子,解释一下。好吧。

abcdefg这个例子:next为-1 -1 -1 -1 -1 -1 -1;

表示的就是最长长度为0.

如果是abcdefga呢?next就是-1 -1 -1 -1 -1 -1 -1 0了。

为什么最后是个0了呢?

~,原因在abcdefga这个里面前面a,后面a,所以长度就是1了。

 

因为初始值为-1,所以-1的就是0,0的就是1,以此类推。。。。。。

 

当我们知道这些next,又有什么用呢?

试想一下?


Abcabdbc这个这个主串如果你匹配到了第二个a的时候出错了。也就是你前面也有abc这三个字符的时候,你觉得你还想要去和第一个b比较吗? abcabcdbcabcdbc   


明显浪费时间啊。我们明明可以去和第二个
c后面的比较啊。不然至少我们也应该和第二个a比较吧。 abcabcdbc abcdbc  

也就是这样

虽然abc只有三个字符,但是如果相同字符达到一万个呢?你还会不会小看这点时间?这就是next的作用。 abcabcdbc   abcdbc

之后的寻找,其实就是普通的找了,不过加了一个next的作用而已。

也就实现了时间复杂度的缩小的作用了。

代码版署:

要想重复的搜索,有多少个只需要,找到之后让k = -1,或者0.重新找。

下面的是我的一个朋友的代码,从-1开始的next代码!

#include <cstdio>#include <iostream>#include <algorithm>#include <vector>#include <set>#include <map>#include <string>#include <stack>#include <queue>#include <cstring>using namespace std;#define REP(I,N) for (I=0;I<N;I++)#define rREP(I,N) for (I=N-1;I>=0;I--)#define rep(I,S,N) for (I=S;I<N;I++)#define rrep(I,S,N) for (I=N-1;I>=S;I--)#define FOR(I,S,N) for (I=S;I<=N;I++)typedef unsigned long long ULL;typedef long long LL;const int INF=0x3f3f3f3f;const LL INFF=0x3f3f3f3f3f3f3f3fll;const LL M=500+7;const LL N=500+7;const double eps=0.00000001;LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}template<typename T>inline T abs(T a,T b) {return a>0?a:-a;}LL n,m;char s[M],a[N];LL Next[N];LL i,j,k,t;void init(char *a,LL *Next){    Next[0]=-1;    int len=strlen(a);    register int i,j;    FOR(i,1,len-1){        j=Next[i-1];        while (j>=0&&a[j+1]!=a[i]) j=Next[j];        if (a[i]==a[j+1]) Next[i]=j+1;        else Next[i] = -1;    }}int kmp(char *s,char *a,LL *Next){    int Len=strlen(s),len=strlen(a);    register int i,j=-1;    REP(i,Len){        while (j>=0&&a[j+1]!=s[i]) j=Next[j];        if (s[i]==a[j+1]) j++;        if (j==len-1) return i-len+1;    }    return -1;}int main(){while (~scanf("%s%s",&s,&a)){        init(a,Next);        n=strlen(a);        REP(i,n) printf("%d ",Next[i]);        t=kmp(s,a,Next);        if (~t) printf("%d",t+1);        else printf("Not Found!");        puts("");}}

之后的代码,是学校的以0为开头的KMP算法,有可能看不懂,不过没关系,看得懂前面的就可以了。(next[i],表示的是0到i-1的位置的相同长度!)

#include <iostream>#include <stdio.h>#include <string.h>#include <math.h>#include <algorithm>#include <map>#include <set>using namespace std;void get_next(char *str,int len,int *next){    next[0] = 0;    next[1] = 1;    int k = 0;    for(int i=2; i<len; i++)    {        k = next[i-1];        while(k>1 && str[i-1]!=str[k-1])            k = next[k];        if(str[i-1] == str[k-1])            next[i] = k+1;        else            next[i] = 1;    }}void print_next(int *next,int len){    for(int i=0; i<len; i++)        printf("%d ",next[i]);    printf("\n");}int KMP(char *str,int slen,char *vision,int len,int *next){    int j = 0;    for(int i=0; i<slen; i++)    {        while(j>0 && str[i] != vision[j])            j = next[j]-1;        if(str[i] == vision[j])            j++;        if(j==len)            return i-len+1;    }    return -1;}int main(){    char str[100],vision[100];    int slen,len,next[100];    while(scanf("%s%s",&str,&vision)!=EOF)    {        len = strlen(vision);        slen = strlen(str);        get_next(vision,len,next);        print_next(next,len);        printf("%d\n",KMP(str,slen,vision,len,next));    }    return 0;}

代码版署,也就结束了。学校的测试过了。不过我希望博友们,可以留言,指正一下可能的错误点或者测试用例。

  最后留言:

        谢谢,各位大佬!(如果人生有3600秒,那么我只想说你现在属于我!哈哈)

原创粉丝点击