字符串的模式匹配算法(BF+KMP)

【前言】

著名的模式匹配算法有BF算法和KMP算法。本文章主要着重讲KMP算法。

【BF算法】

BF(Brute-Force)算法,是最简单直观的模式匹配算法。

灵魂画手已上线~哈哈哈哈或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或或

上边的是主串a,下边的是模式串b。分别用i,j做各自串的指针,若a[i]==b[j],i++,j++,继续比较;若不相等,i=i-j+2,j=1,重新开始匹配。


如:第三次匹配的时候a[3]='1',b[3]='3',不相等,则指针后退,i=i-j+2=2,j=1,重新从头开始匹配。

此算法最好情况复杂度为O(n+m),最坏情况复杂度为O(n*m)。算法复杂度较高。

【KMP算法】

如上图所示,我们发现后退进行的比较有些是不必要的,比如第三趟的时候,我们只需要从i=3,j=1进行比较即可。由此作出改进:每当一趟匹配过程中出现不等时,不需要回溯i指针,而是利用已经得到的部分匹配的结果将模式串向右滑动尽可能远的一段距离后,继续进行比较。这时,我们就需要定义一个数组next[]用来记录模式串T中第j个字符与主串S中相应字符失配时,在模式串中需重新和主串中该字符进行比较的字符的位置,即模式串从T[i]-T[j-1]最大前缀后缀相同的长度+1。

next[] :当j=1时,next[j]=0;当1<k<j时,next[j]=MAX{k|"T1 T2...Tk-1"="Tj-k+1 Tj-k+2...Tj-1"}.

如:

     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

【完整代码】

#include<stdio.h>
#include<string.h>
const int maxn=1005;
char S[maxn],T[maxn];
int next[maxn];
int Slen,Tlen;
void get_next()
{
    Slen=strlen(S)-1;
    Tlen=strlen(T)-1;
    int i=1,j=0;
    next[1]=0;
    while(i<=Tlen)
    {
        if(j==0||T[i]==T[j])
            next[++i]=++j;
        else
            j=next[j];
    }
    printf("next[]:\n");
    for(i=1;i<=Tlen;i++)
        printf("%d ",next[i]);
    printf("\n");
}
void KMP()
{
    get_next();
    int i=1,j=1;
    while(i<=Slen&&j<=Tlen)
    {
        if(j==0||S[i]==T[j])
            i++,j++;
        else
            j=next[j];
    }
    if(j>Tlen)
        printf("匹配成功!\n初始位置为%d\n",i-Tlen);
    else
        printf("匹配失败!\n");
}
int main()
{
    S[0]=1,T[0]=1;
    while(~scanf(" %s %s",S+1,T+1))
        KMP();
    return 0;
}

【再改进】

前面定义的next[]数组仍有缺陷。例如模式串“aaaab”在和主串“aaabaaaab”匹配时,当i=4,j=4时不相等,还需进行i=4,j=3,2,1这三次比较。实际上,因为模式串中第1-3个字符和第四个字符都相等,因此不需要再和主串中第四个字符相比较,而可以直接进行i=5,j=1的比较。也就是说,若按上述定义得到next[j]=k,而模式串中Tj=Tk,则当主串中字符Si!=Sj时,不需要再和Tk进行比较,而直接和Tnext[k]进行比较,换句话说,此时的next[j]应和next[k]相同。

如:

     j             1   2   3   4   5                                     j             1   2   3   4   5   6   7   8  

模式串         a   a   a   a   b                                 模式串        a   b   a   a   b   c   a   c

next[j]         0   1   2   3   4                                 next[j]         0   1   1   2   2   3   1   2

nextval[j]     0   0   0   0   4                                 nextval[j]     0   1   0   2   1   3   0   2

【改进后代码(只改动了自定义函数next())】

#include<stdio.h>
#include<string.h>
const int maxn=1005;
char S[maxn],T[maxn];
int nextval[maxn];
int Slen,Tlen;
void get_nextval()
{
    Slen=strlen(S)-1;
    Tlen=strlen(T)-1;
    int i=1,j=0;
    nextval[1]=0;
    while(i<=Tlen)
    {
        if(j==0||T[i]==T[j])
        {
            if(T[++i]!=T[++j])
                nextval[i]=j;
            else
                nextval[i]=nextval[j];
        }
        else
            j=nextval[j];
    }
    printf("nextval[]:\n");
    for(i=1;i<=Tlen;i++)
        printf("%d ",nextval[i]);
    printf("\n");
}
void KMP()
{
    get_nextval();
    int i=1,j=1;
    while(i<=Slen&&j<=Tlen)
    {
        if(j==0||S[i]==T[j])
            i++,j++;
        else
            j=nextval[j];
    }
    if(j>Tlen)
        printf("匹配成功!\n初始位置为%d\n",i-Tlen);
    else
        printf("匹配失败!\n");
}
int main()
{
    S[0]=1,T[0]=1;
    while(~scanf(" %s %s",S+1,T+1))
        KMP();
    return 0;
}

【结语】

总算搞懂了

累死了累死了哇我是猪脑壳哭哭



猜你喜欢

转载自blog.csdn.net/qq_41117236/article/details/80771027