KMP算法理解与C++代码实现

参考:https://www.bilibili.com/video/BV1i64y1M7zv?from=search&seid=4229951256989443915

用途:在主串(长度n)中快速匹配到模式串位置(长度m)
比起暴力法(复杂度O(n*m)),KMP的复杂度可以是O(n+m)
关键在于发生不匹配时如何更好地选择子串的下一个比较字符的位置(暴力法是从子串的初始位置重新开始,而KMP利用了已匹配的信息)

所以问题的关键在于如何求解当发生不匹配时,如何选取子串的下一个比较字符的位置,而这个位置实际上就是截止到上一个字符子串的最长公共前后缀的长度,这些信息存放在next数组中,因此问题转换成求解next数组。
子串最长公共前后缀长度,要小于选取长度,因为本身肯定是公共前后缀, 但是这没有意义

求解next数组:
如果暴力求法复杂度是O(m*m),但是可以通过动态规划让复杂度下降到O(m),获取next数组的代码如下:
next[i]:截止上一个索引i-1的最长公共前后缀长度,也是如果在索引i位置出现不匹配时,下一个需要去匹配的子串的位置
特别注意不要拿string::size_t和int类型数据比较,会导致一些问题,比如int为负数时

void getNext(const string &s,vector<int> &next){
    int j=0,k=-1;
    next[0] = -1;
    int sz = s.size();
    while(j<sz-1){
        if(k==-1 || s[j]==s[k]) next[++j] = ++k;
        else k=next[k]; // 当前字符出现不匹配,那么退而求其次,再去比较稍微短一点的前后缀,也是就当前匹配前后缀的公共前后缀长度
    }
}

完整程序如下:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

void getNext(const string &s,vector<int> &next){
    int j=0,k=-1;
    next[0] = -1; //初始值为-1是为了使后面处理方便
    int sz = s.size();
    while(j<sz-1){
        if(k==-1 || s[j]==s[k]) next[++j] = ++k;
        else k=next[k];
    }
}

int kmp(const string &s1,const string &s2){
    int i=0,j=0;
    int sz1 = s1.size(),sz2 = s2.size(); //注意不要拿string::size_t和int类型数据比较,会导致一些问题,比如int为负数时
    vector<int> next(s2.size());
    getNext(s2,next);
    while(i<sz1 && j<sz2){
        if(j==-1 || s1[i]==s2[j]){
            ++i;
            ++j;
        }else
            j = next[j];
    }
    if(j>=s2.size()) return(i-sz2);
    else return -1;
}
int main() {
    string s1("abdbabde");
    string s2("babd");
    cout << kmp(s1,s2) << endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值