KMP算法

首先简单的提一下BF算法(定义不加阐述):

在进行模式匹配时,i(主串) 下标的值与 j (子串)下标的值进行比较,若相等则 i 和 j 挪到下一位,若失配的话则 j 回退到 0 号下标位置,i 回退到 i - j +1的位置,循环往复

这种算法是解决模式匹配的一种直接算法,以图为例,我们可以看到,失配字符前的字符没有 a ,所以 i ,j 回退完全就是浪费时间。对于此 ,我们对此算法进行优化,就是KMP算法(定义不加阐述),KMP算法在失配时,不需要回退 i  ,只需将 j  回退到“合适位置”

关键在于这个合适位置,下面我们来找找这个合适位置,方法叫做next[]数组法;我们定义一个next[]数组,在这我们需要了解前缀和后缀,因为我们next[]数组是用来保存最大前缀和最大相等后缀的值的,在next[]数组中,next[]数组中数字不仅代表最大前缀 和 后缀相等数,也代表 子串中 j 要回退的位置,这就是所谓的那个合适位置

比如:abcjkdabc,那么这个数组的最长前缀和最长后缀相同是abc。 
cbcbc,最长前缀和最长后缀相同是cbc。 
abcbc,最长前缀和最长后缀相同是不存在的。 

我们以上图为例,很多人可能不明白下边那一行数字咋来的,在这我们进行分析:

在定义next[]数组时,我们定义 j=0时,k=-1, next[0] = -1 (含义相同),j=1时的next[1]=++k,查找假设在第二个b处失配,即第一个b与 0号下标a相比,失配时值记为 0,存在第二个b下,同理后边数字也是一样的道理

KMP算法核心:在进行模式匹配时,若匹配不成功,i 不需要回退,j 需要回退到最大前缀和最大后缀相等的值处,即 j 所指向当前的next[k]的值处,然后让 j 所指向的值与 i 值进行匹配,(便于理解可以认为让 j 指向的值与 i 指向的值对齐进行比较)若回退的值为 --1时,则说明在主串 i 位置之前的数据都失配,这时,让主串中 i++,子串中j++

以上图为例,执行KMP算法进行模式匹配

代码实现如下:

void GetNext(PString ps, int* next)
{
    if (ps == NULL)
    {
        return;
    }
    int k = -1;
    int i = 1;
    int len = ps->curlen;

    if (len == 0)
    {
        return;
    }
    next[0] = -1;
    if (len == 1)
    {
        return;
    }
    next[1] = ++k;

    while (i < len - 1)
    {
        if (k == -1 || ps->pstr[i] == ps->pstr[k])
        {
       
            next[++i] = ++k;
        }
        else
        {
            k = next[k];
        }
    }
}
int KMP(PString ps1, PString ps2)
{
    if (ps1 == NULL || ps2 == NULL)
    {
        return -1;
    }
    int len1 = ps1->curlen;
    int len2 = ps2->curlen;
    int i = 0;
    int j = 0;
    int* next = (int*)malloc(sizeof(int)*len2);
    GetNext(ps2, next);

    while (i < len1 && j < len2)
    {
        if (j == -1 || ps1->pstr[i] == ps2->pstr[j])
        {
            ++i;
            ++j;
        }
        else
        {
            j = next[j];
        }
    }
    free(next);
    if (j >= len2)
    {
        return i - j;
    }
    return -1;
}
 

另附BF算法:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值