模式匹配之BF算法与KMP算法

模式匹配之BF算法与KMP算法

完全只是想方便自己复习所用,如有错误还请指出以及不详细还望见谅,能力有限

2020.4.23更新:主要针对next数组的含义,以及求法进行了进一步学习
ps:关键是书上的字符串都是从一开始,本人喜欢从0开始,而且目前觉得代码不长,习惯一个函数写到底(当然这并不好),在当中还踩了很多坑


一、简单模式匹配算法(BF算法)


通俗一点,就是暴力搜索,当发现主串与模式串相对应字符不一样时,指针回溯,从主串下一个位置再开始逐一匹配,话不多说,看代码:

#include
#include
int main()
{
    char s1[110];
    char s2[110];
    while(~scanf("%s%s",s1,s2))
    {
        int len1=strlen(s1);
        int len2=strlen(s2);
        int i=0;
        int j=0;
        while(i<len1&&j<len2)
        {
            if(s1[i]==s2[j])
            {
                ++i;
                ++j;
            }
            else
            {
                i=i-j+1;      //指针回溯到起初匹配的第一个字符的下一个字符
                j=0;          //模式串从头开始
            }
        }
        if(j==len2)       //由于后面j自加了
        {
            printf("%d\n",i-len2+1);     //由于字符串从零开始,所以+1
        }
        else
            printf("0\n");
    }
    return 0;
}

高级的来了,KMP算法老大哥登场


二、KMP算法


KMP算法最核心的就是求next函数,这个就得自己好好理解了,我是觉得书上的公式没什么用,还是自己理解来的好

含义:

1.首先,为什么需要next[]数组?
原因:为了不重复访问字符串。即:每次都是从失配位开始(有点懵?,不要紧,继续往下看)
2.具体用法:
需要明确的是:next[]是针对模式串(也就是“短”的串,待匹配的串)而言,而非主串
对模式串求0~i位的最长公共前后缀的长度,是为了在i+1位是匹配失败了,找到它的匹配位,而不是重新从最前面开始
假设模式串s2的x位开始匹配,那么模式串的前x-1位需要和主串的失配位的前x相同,这就是找模式串的最长公共前后缀的长度

next[i]:表示模式串前i-1位比它本身短的最长公共前后缀的长度
何为最长公共前后缀?
例如,字符串:abaabc,每个子串对应的比它本身短的最长公共前后缀的长度
(这是字符串从0开始编号)

a b a a b c
0 0 1 1 2 0

然后对于next[]数组就为(由于是前i-1位,于是我们只需要整体向右移一位即可):

a b a a b c
-1 0 0 1 1 2

代码如下:

#include
#include
int main()
{
    char s1[110];
    char s2[110];
    while(~scanf("%s%s",s1,s2))
    {
        int next[110];
        next[0]=-1;        //由于书上的字符串是以1开头所以next[0]=0,而我是以0开头,所以next[0]=-1,至于为啥,我觉得自己手动算下应该就明白了
        int i=0;
        int j=-1;            //重点!!!!!
        int len1=strlen(s1);
        int len2=strlen(s2);
        while(i<len2)
        {
            if(j==-1||s2[i]==s2[j])     //由于next[0]=-1,所以对j==-1特判下
            {
                ++i;
                ++j;
                next[i]=j;
            }
            else
            {
                j=next[j];
            }
        }
        i=0;
        j=0;
        while(i<len1&&j<len2)
        {
            if(s1[i]==s2[j]||j==-1)    //同样由于next[0]=-1的存在
            {
                ++i;
                ++j;
            }
            else
            {
                j=next[j];   //这里就不需要指针回溯,大大降低了时间复杂度
            }
        }
        if(j==len2)
        {
            printf("%d\n",i-len2+1);
        }
        else
            printf("0\n");
    }
    return 0;
}

突然发现,感觉似乎懂的比以前多那么一点了

你可能感兴趣的:(数据结构与算法)