KMP算法

如果给我们两个字符串“str_a”和“str_b”,“str_a”是主串,“str_b”是子串,需要在主串中找到子串出现的位置。

平常的朴素算法是:“i,j”分别指向主串和子串,一个一个判断是否字符相等,如果出现不相等的情况,子串对应的“j”就复原到子串的开头位置,然后继续向下对比,这样的时间复杂度是O(n*m),为了简化时间复杂度,引入KMP算法。


KMP算法的重点在于“Next数组”,有两点个人认为很重要:其一是“Next数组”的作用;其二是“Next数组”的写法。

1.Next数组的作用

用一个例子做演示:主串为“A B C A B C A B C D”,子串为“A B C A B C D”。

当子串中的“D”与主串中的“A”(第三个A)相对应的时候,会发生失配的情况,而此时子串中有“后缀”和“前缀”相等的情形发生,由于子串中“D”前面的所有字符都与主串中的部分相匹配,那么可以考虑直接将“前缀”放在“后缀”的位置进行下一次比较来有效地减少比较次数来减少时间复杂度(若一个一个比较向后移动会进行过多地无用的比较,而直接跳转却很有效)。

2.Next数组的写法

首先,要知道“Next数组”是用来记录子串“str_b”中存在的“后缀”的“前缀”字符的位置,实际上是为失配时跳转起作用的。

用“A B C A B C D”为例,str_b[7] = {A, B, C, A, B, C, D}; 在“Next数组”中,Next[0] = -1;(原因后面解释) 若出现后面与前缀相匹配,那么后面的字符的再后一个的值就是当前这个字符的值再加一,若出现与前缀失配的情况,前缀指向的那个位置就回到“Next”值的位置。

用“j”表示前缀的指针,“k”表示后缀的指针。

j = -1, k = 0; 

当str_b[j] == str_b[k]时,Next[k + 1] = Next[k] + 1;

当str_b[j] != str_b[k]时,此时发生失配情况,j需要回溯,即 j = Next[j]; 注意可能会出现回到非 -1 的情况!此时可能需要连续回溯,这种情况多发生在连续失配时。

这里说明一下“-1”的作用,由于总会出现连续的失配情况,就是指第一个字符连续失配,因为每次都会有 j++ k++ 的进行,由 -1 变成 0 之后,仍然会有 str_b[0] 为第一个字符再次拿去与后面的字符相比较, 不会出现停滞不前的情况,也具有符合下标的要求。

代码实现

代码实现的时候要注意的是“KMP函数”中的跳转,还有“Next函数”的写法。

#include <cstdio>
#include <iostream>
using namespace std;

char b_str[55];
char a_str[55];
int Next[55];

void get_next(){
    int len = strlen(b_str);
    int j = 0, k = -1;
    Next[0] = -1;	//第一项赋为0
    while(j < len - 1){		//注意 j 最大到 len — 1 ,计算完这个值后就应当结束循环
        if(k == -1 || b_str[j] == b_str[k]){
            k++;
            j++;
            Next[j] = k;
        }
        else
            k = Next[k];
    }
}

int kmp(){
    int len_a = strlen(a_str);
    int len_b = strlen(b_str);
    int i = 0, j = 0;	//下标从0开始
    while(i < len_a && j < len_b){	//i 或 j 都跑完就OK了
        if(j == -1 || a_str[i] == b_str[j]){	//如果 j 回溯到 -1(从头比较) 或者 相等都会 +1 处理
            i++;
            j++;
        }
        else
            j = Next[j];
    }
    if(j == len_b)
        return i - j + 1;
    else
        return -1;
}

int main(){
    memset(Next, 0, sizeof(Next));
    cin >> a_str;
    cin >> b_str;
    get_next();
    cout << kmp() << endl;
    return 0;
}
给出两个讲的比较详细的地址:

http://www.cnblogs.com/yjiyjige/p/3263858.html

https://www.cnblogs.com/tangzhengyue/p/4315393.html


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值