BF,KMP

BF,KMP

BF和KMP算法都是针对于字符串的操作,是用于字符串匹配的,从主串中查找有无子串的存在,若匹配成功,返回主串中子串首元素的下标,匹配失败返回-1

BF算法

时间复杂度 :O(mn)

BF算法属于一种暴力破解的算法,穷举所有可能来进行匹配,在其算法过程中存在回溯,这很影响算法效率,因此实际运用情况很少,更多选择其他算法,如KMP

BF算法是通过循环,从主串的第一个元素开始,与子串的第一个元素进行匹配,若匹配成功,则让主串的第二个元素与子串的第二个元素进行匹配,依次类推匹配,若匹配失败,则让主串的下一个元素与子串的首元素重新开始匹配,后面依次类推,直到完全匹配成功,返回下标,或匹配失败,返回-1

int BF(const char *s,const char *sub,int pos)
{
    int slen=strlen(s);
    int sublen=strlen(sub);
    int i=pos;
    int j=0;
    
    while(i<slen && j<sublen)
    {
        if(s[i]==sub[j])
        {
            i++;
            j++;
        }
        else
        {
            i=i-j+1;
            j=0;
        }
    }
    if(j>=sublen)
    {
        return i-j;
    }
    return -1;
}

KMP算法

时间复杂度 :O(m+n)

KMP算法是一种改进的字符串匹配算法,与BF不同的是,在匹配失败的情况下,只有子串回退,而主串不回退,它是利用匹配失败后的信息尽量减少了主串与子串的匹配次数,从而大大提高了算法效率。

KMP的核心就在于求出next数组,该数组保存了子串中每个位置匹配失败时,子串应当回退到什么位置继续与主串进行匹配,这里暂且称某位置匹配失败应该回退的位置下标称作K值,以下图为例,当在红框位置出现匹配失败时,主串不回退,假设子串应回退的位置下标为k,那么k之前的部分与当前主串位置前的相同长度的一部分也应是相等的,如此才有从该位置继续匹配的意义,如紫线部分,因为直到红框才出现匹配失败,可知,绿线部分是相等的,匹配成功的,因此,截取对应段时也该是相等的,我们截取与k前片段相同长度的片段,如黑线部分,推导一下可知,子串中紫线与黑线部分应该是相等的,所以k值应该是匹配失败位置前的片段中,以子串首元素开头,匹配失败位置前一元素为尾的最长的两个真子串的长度,即最长公共前后缀的长度。

k值为next[j]为j下标前 字符串 的最长公共前后缀长度

在这里插入图片描述

以下图为例求该字符串的next数组,手动推导可知,子串中0下标前面已经没有元素,匹配失败也仍该是从0下标继续匹配,故0下标的k值为0,但为后面的代码方便,将其设为-1也可,即next[0]=-1;1下标匹配失败时,前面只有一个元素,故也没有最长公共前后缀,所以next[1]=0;这两个值是固定的。

在这里插入图片描述

假设子串中匹配到的位置为j,此时 j == 1,k == 0;求2下标即 j+1 的k值。当在j+1位置才匹配失败时,此时分两种情况:

若j下标元素与j位置k值下标元素相等:即j+1前字符串最长公共前后缀长度,相当于 j 前最长公共前后缀长度+1,因为前缀与后缀后面增加的元素相等,所以next[j+1]=next[j]+1即 next[j+1]= j的k + 1,如下图求2下标k值

在这里插入图片描述

若 j 下标元素与j位置k值下标元素不相等:如下图求3下标k值时,此时 j == 2,k == 1,此时可类比考虑为,j前字符串前缀与后缀各加上一元素进行匹配,在最后元素位置出现匹配失败,即绿框部分进行字符串匹配在紫框位置匹配失败,子串应进行回退,k下标进行回退,而应该回退的位置我们是知晓的,因为前面元素的next值即K下标的k值我们已经求出来了,故此时 k=next[k];

在这里插入图片描述

static void Getnext(const char *sub,int *next)
{
    next[0]=-1;
    next[1]=0;
    int j=1;
    int k=0;
    int sublen=strlen(sub);
    while(j+1<sublen)
    {
        if(sub[j]==sub[k] || k==-1)
        {
            next[++j]=++k;
        }
        else
        {
            k=next[k];
        }
    }
}
int KMP(const char *s,const char *sub,int pos)
{
    int slen=strlen(s);
    int sublen=strlen(sub);
    int i=pos;
    int j=0;
    int *next=(int *)malloc(sizeof(int)*sublen);
    Getnext(sub,next);
    
    while(i<slen && j<sublen)
    {
        if(s[i]==sub[j] || j==-1)
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    free(next);
    if(j>=sublen)
    {
        return i-j;
    }
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值