KMP算法

在了解KMP算法之前必须先了解bf算法
这里写图片描述

int bf(char *str, char *substr, int index)  // index是用户指定从str这个串中任意位置开始匹配
{
    int slen = strlen(str);
    int sublen = strlen(substr);
    int i = index;
    int j = 0;
    while (j<sublen)
    {
        if (i >= slen)
            return -1;
        if (str[i] == substr[j])
            i++,j++;
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    return i - j;
}

这里写图片描述

有了上面的思路,我们很容易写出下面程序(占时先不考虑index的回退数组)

int KMP(char *str, char *substr, int index)
{
    int slen = strlen(str);
    int sublen = strlen(substr);
    int i = index;
    int j = 0;
    int *next;
    getNext(substr, next);
    while (j<sublen)
    {
        if (i >= slen)
            return -1;
        if (-1 == j || str[i] == substr[j])
        {
            i++; j++;
        }
        else
            j = next[j];
    }
    return i - j;
}

相等的时候 i j 都++ 不等的时候 j 就回退到next[j],现在我们只需要求出这个回退数组就好了

void getNext(char *substr, int *&next)
{
    int sublen = strlen(substr);
    if (sublen == 0)
        return;
    next = new int[sublen];
    next[0] = -1;//第一个角标我们默认初始化为-1 
    if (sublen == 1)
        return;
    next[1] = 0;//第二个角标初始化为0

    /*弄两个角标出来,一前一后,求的是i + 1位置匹配失败应该回退的位置*/ 
    int i = 1, k = 0; 
    while (i < sublen)
    {
        if (-1 == k || substr[k] == substr[i])// i + 1 位置匹配失败但是 i 位置和 k 位置字符相等 
            next[++i] = ++k;
        // else             //                 如果出现 a b c a b a b c
            // next[i] = 0;                 //         -1 0 0 0 1 2 1 2  所以并不是每次都回到 0 位置  这里就回到了1 
        else
            k = next[k];// i+1位置上匹配失败了,并且i 位置 和 k 位置不相等,我们就要回退到 “k位置匹配失败”后回退的位置上去
                        //就相当于把k以前的串当成了子串,i以前的串当成了父串 【为什么可以这样做?k位置匹配失败回退的位置我们已经知道了,k是小于i的】 
    }
}

上面最后一句很难理解,仔细体会吧
下面nextVal是next的升级版本 当出现substr=”aaaaab” 这种情况比上面更高效
当str = “aaaacaaaaab” 我们匹配到c的时候失败,如果是next那么应该跑到k = 3的位置上去,
但实际上3位置也是‘a’,下次比较就属于无效比较,还是会再次回到上一个k,所以有了nextVal进行优化
a a a a a b a a b a
x 0 0 0 0 1 0 0 2 0

void getNextVal(char *substr, int *&nextVal)
{
    int sublen = strlen(substr);
    if (sublen == 0)
        return;
    nextVal = new int[sublen];
    nextVal[0] = -1;
    if (sublen == 1)
        return;
    nextVal[1] = 0;
    int i = 2;
    int k = 0;
    while(i<sublen)
    {
        if(-1 == k || substr[i-1] == substr[k])
        {
            if(substr[i] == substr[i-1])
                nextVal[i] = nextVal[i-1];
            else
                nextVal[i] = ++k;
            i++;
        }
        else
            k = nextVal[k];
    }
}
int main()
{
    char *str = "ababcabcdabcde";
    char *substr = "abcd";
    int res = bf(str, substr, 0);
    cout << "bf :" << res << endl;
    res = bf(str, substr, 6);
    cout << "bf :" << res << endl;

    res = KMP1(str, substr, 0);
    cout << "KMP1 :" << res << endl;
    res = KMP1(str, substr, 6);
    cout << "KMP1 :" << res << endl;

    str = "aaabcaaabcabcf";
    substr = "aaabcabcf";
    //        x001230
    res = KMP1(str, substr, 0);
    cout << "KMP1 :" << res << endl;

    res = KMP2(str, substr, 0);
    cout << "KMP2 :" << res << endl;
}
/*
bf :5
bf :9
KMP1 :5
KMP1 :9
KMP1 :5
KMP2 :5
*/

一般书上出题都会next都是以 0 1 开头
我是以-1 0 开头,按照上面算下来,全部加1就能得到书上的答案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值