BF算法与KMP算法

BF算法

两个字符串 str sub
这里写图片描述
算法基本思想:
初始 i=0,j=0
1、当str[i]==sub[j]时i++,j++;
2、当str[i]!=sub[j]时,i=i-j+1,j=0;
3、当j>=strlen(str)或者i>=strlen(sub)结束

BF 算法的时间复杂度是:O(mn)
m 代表主串的长度, n 代表子串的长度。

代码实现:

int BF(const char *str, const char *sub, int pos)
{
    int lenstr = strlen(str);
    int lensub = strlen(sub);

    if (pos < 0 || pos>lenstr)
    {
        return -1;
    }

    int i = pos;
    int j = 0;

    while (i < lenstr&&j < lensub)
    {
        if (str[i] == sub[j])
        {
            i++;
            j++;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }

    if (j >= lensub)
    {
        return i - j;
    }
    return -1;
}

KMP算法

KMP 算法更为高效

时间复杂度为 O(m+n)
m 代表主串的长度, n 代表子串的长度。

KMP 和 BF 唯一不一样的地方在:主串的 i 并不会回退, 并且 j 也不会移动到 0 号位置。

那j该如何退呢?

当 i 和 j 走到如下图位置

j 不需要回退到0,而是回退到上一个‘c‘的位置即可,假设每次回退为“K”位置。

我们要在确定sub中匹配到那一位不同时回退到对应的k位置,把所有的k都保存在next[strlen(sub)]数组中。

那如何确定每次回退到哪呢?
如上图i=j=5时,str[i]!=sub[j],(用P0表示str[0]的字符,用S0表示sub[0]的字符)所以可以保证
P0P1P2P3P4=S0S1S2S3S4
这是观察可得应把j回退到S2即可,由于S0S1==P3P4==S3S4
同理假设str[i]!=sub[j]

P0P1P2,,,,,Pi ==S0S1S2,,,,,Sj
回退到k
存在S0,,,Sk-1 == Pi-k,,,Pi-1==Sj-k,,,,,Sj-1
第j位不同时,回退到k位置
so, next[j] = k ————条件1
由于当0号下标不同时,我们就会让j回退到当前位置,
但是默认next[0]=-1———条件2
由于当1号下标不同时,我们就会让j回退到0号位置,
so,next[1]=0———条件3
我们要根据已知的三个条件求 next[2] next[3],,,,,,,直到求出所有的next数组

由于Sj 与 Sk只存在两种关系 1、Sj = Sk 2、Sj != Sk
根据这两种关系我们做出两种假设

假设1、Sj = Sk
这时
S0,,,Sk-1 == Pi-k,,,Pi-1==Sj-k,,,,,Sj-1 【1】就可以拓展为
S0,,,Sk-1Sk ==Sj-k,,,,,Sj-1Sj 【2】

由于在【1】==>next[j] = k
so, 【2】==>next[j+1]=k+1

假设2、Sj != Sk ,如下图S2!=S5
这里写图片描述

这时由多次观察总结得
S5本来按Sj==Sk,应该回退到2位置但是很明显,S2!=S5,这时让k继续回退到2的k值====>k=next[k]

如此,就能求得所有next数组的值

每次遇到不同,让j回退到next[j]

代码实现

void GetNext(char *sub,int *next)
{
    int lensub = strlen(sub);
    next[0] = -1;
    next[1] = 0;
    int i = 2;//第二项
    int k = 0;
    while (i < lensub)
    {
        if ((k == -1) || sub[k] == sub[i -1])
        {
            next[i] = k + 1;
            i++;
            k++;
        }
        else
        {
            k = next[k];
        }
    }
}

int KMP(char *str, char *sub, int pos)
{
    int lenstr = strlen(str);
    int lensub = strlen(sub);

    if (pos < 0 || pos>lenstr)
    {
        return -1;
    }

    int i = pos;
    int j = 0;

    int *next = (int *)malloc(sizeof(int)*strlen(sub));
    assert(next != NULL);
    GetNext(sub, next);
    while (i < lenstr &&j < lensub)
    {
        if ((j == -1) || str[i] == sub[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
    }
    if (j >= lensub)
    {
        return i - j;
    }
    return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值