KMP算法

来源:互联网 发布:preview.exe软件下载 编辑:程序博客网 时间:2024/05/18 01:05

KMP算法

假设主串:S: S[1] S[2] S[3] ……S[n]

模式串:T: T[1] T[2] T[3]…..T[m]

现在我们假设主串第i 个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i 个字符与模式串的第k(k<j)个字符继续比较,此时就有S[i] != T[j]

主串:   S[1]...S[i-j+1]...S[i-1]S[i]...

                    ||(匹配)   ||    ≠

模式串:            T[1]...  T[j-1] T[j]

由此,可以得到关系式如下

  T[1]T[2]T[3]...T[j-1] = S[i-j+1]...S[i-1]

由于S[i] != T[j],接下来S[i]将与T[k]继续比较,则模式串中的前k-1咯字符串必须满足下列关系式,并且不可能存在k'>k满足下列关系式:

T[1]T[2]T[3]...T[k-1] = S[j-k+1]S[j-k+2]...S[i-1] (k<j)

也就是说:

主串:  S[1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

                    ||        ||         ||    ?(待比较)

模式串:           T[1]      T[2]...  T[k-1] T[k]

现在可以把前面的关系综合总结如下

S[1]...S[i-j+1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

            ||          ||       ||           ||   ≠

           T[1]...    T[j-k+1] T[j-k+2]...   T[j-1] T[j]

                         ||       ||            ||    ?

                        T[1]     T[2] ...     T[k-1] T[k]

现在唯一的任务就是如何求k了,通过一个next函数求。

/*pku3461(Oulipo), hdu1711(Number Sequence)这个模板 字符串是从0开始的Next数组是从1开始的*/#include <iostream>#include <cstring>using namespace std;const int N = 1000002;int next[N];char S[N], T[N];int slen, tlen;void getNext(){    int j, k;    j = 0; k = -1; next[0] = -1;    while(j < tlen)        if(k == -1 || T[j] == T[k])            next[++j] = ++k;        else            k = next[k];}/*返回模式串T在主串S中首次出现的位置返回的位置是从0开始的。*/int KMP_Index(){    int i = 0, j = 0;    getNext();    while(i < slen && j < tlen)    {        if(j == -1 || S[i] == T[j])        {            i++; j++;        }        else            j = next[j];    }    if(j == tlen)        return i - tlen;    else        return -1;}/*返回模式串在主串S中出现的次数*/int KMP_Count(){    int ans = 0;    int i, j = 0;    if(slen == 1 && tlen == 1)    {        if(S[0] == T[0])            return 1;        else            return 0;    }    getNext();    for(i = 0; i < slen; i++)    {        while(j > 0 && S[i] != T[j])            j = next[j];        if(S[i] == T[j])            j++;        if(j == tlen)        {            ans++;            j = next[j];        }    }    return ans;}int main(){        int TT;    int i, cc;    cin>>TT;    while(TT--)    {        cin>>S>>T;        slen = strlen(S);        tlen = strlen(T);        cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()<<endl;        cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl;    }    return 0;}/*test caseaaaaaa aabcd daabaa b*/


下面是几个例子

HDU 1711

找子串首次出现的位置:

#include<stdio.h>int a[1000010],b[10010];int next[10010];int n,m;void getNext(){    int j,k;    j=0;    k=-1;    next[0]=-1;    while(j<m)    {        if(k==-1||b[j]==b[k])          next[++j]=++k;        else k=next[k];    }    }  //返回首次出现的位置 int KMP_Index(){    int i=0,j=0;    getNext();        while(i<n && j<m)    {        if(j==-1||a[i]==b[j])        {            i++;            j++;        }            else j=next[j];            }        if(j==m) return i-m+1;    else return -1;}      int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        for(int i=0;i<n;i++)          scanf("%d",&a[i]);        for(int i=0;i<m;i++)          scanf("%d",&b[i]);        printf("%d\n",KMP_Index());    }        return 0;}


/*POJ   3461 Oulipo统计子串出现的次数*/#include<stdio.h>#include<string.h>#include<iostream>using namespace std;char W[10010],T[1000010];int wlen,tlen;int next[10010];void getNext(){    int j,k;    j=0;    k=-1;    next[0]=-1;    while(j<wlen)    {        if(k==-1||W[j]==W[k])        {            next[++j]=++k;        }        else k=next[k];    }}int KMP_count(){    int ans=0;    int i,j=0;    if(wlen==1&&tlen==1)    {        if(W[0]==T[0])return 1;        else return 0;    }    getNext();    for(i=0;i<tlen;i++)    {        while(j>0&&T[i]!=W[j])          j=next[j];        if(W[j]==T[i])j++;        if(j==wlen)        {            ans++;            j=next[j];        }    }    return ans;}int main(){    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    int tcase;    scanf("%d",&tcase);    while(tcase--)    {        scanf("%s%s",&W,&T);        wlen=strlen(W);        tlen=strlen(T);        printf("%d\n",KMP_count());    }    return 0;}



/*POJ 1611 PeriodSample InputaaaaabaabaabaabSample OutputTest case #1 2 3Test case #2 2 2 3 4题意就是求一个字符串中的重复子串*/#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>using namespace std;const int MAXN=1000010;char str[MAXN];int next[MAXN];int n;void getNext(){    int j,k;    j=0;    k=-1;    next[0]=-1;    while(str[j]!='\0')    {        if(k==-1||str[j]==str[k])        {            j++;            k++;            if(j%(j-k)==0&&j/(j-k)>1)              printf("%d %d\n",j,j/(j-k));            next[j]=k;        }        else k=next[k];    }}int main(){   // freopen("in.txt","r",stdin);   // freopen("out.txt","w",stdout);    int iCase=0;    while(scanf("%d",&n),n)    {        iCase++;        scanf("%s",&str);        printf("Test case #%d\n",iCase);        getNext();        printf("\n");    }    return 0;}



原创粉丝点击