kmp算法

本文只为自己记住kmp算法,在csdn留个笔记方便以后自己找得到,只留核心点,不做过多解释,需要学习kmp算法的看其他博客哈。

next数组的含义:
next[6]=3 表示pattern[0:6](闭区间,包含头尾)中,后缀串等于前缀串的最长后缀串长度为3.下面说成最长相等前缀串或者最长相等后缀串为3。
比如
abcdabc。最长相等后缀串是abc,长度为3。后缀abc长度为3,他等于前缀。他已经是最长的了,更长的后缀dabc,他不等于前缀abcd。

kmp算法的第一步骤就是求模式串的next数组。

其中规定next[0] = 0。因为前缀后缀不能是同一字符串。
然后用迭代的方法算出next[1]到next[pattern.size()-1]。
记j为本次要算的next值。即本次要计算next[j],
记k为上次算出的next值。即k = next[j-1]。k一方面表示,比待计算的字串长度短1的字串,他的最长相等后缀串长度为k。同时,他也表示比待计算的字串长度短1的字串,他的最长相等前缀串的下一个字符的下标。如图
在这里插入图片描述
计算方法如下:
情况1:如果 pattern[j] = pattern[k]
那么next[j] = k+1,同时,把k更新为 k = next[j]。同时,把j更新为j = j+1。

如果 pattern[j] != pattern[k],那就得找到比上图蓝色部分更短的字串。他的长度最多是next[ k-1 ] (如果这个更短的字串存在,他肯定以上图中左边的蓝色部分的最后一个字符结尾)。做法就是把k更新为next[k-1]。然后再次判断pattern[j] 是否 = pattern [k],如果相等,说明这个更小的字串是合适的,那么next[j] = k+1,同时,把k更新为 k = next[j]。同时,把j更新为j = j+1。(情况1)。如果不相等,说明这个比蓝色还短的字串还不够短,就继续 k = next[k-1]。这个步骤持续到 k0时。因为当k0时,next[k-1]=next[-1]是不合法的。当k =0时,他们还不相等,那么就让next[i] = 0。表示没有相同前后缀。
用代码表示就是

vector<int> next(pattern.size(),0);
    next[0]=0;
    int k=next[0];
    for(int i=1;i<pattern.size();){
        if(pattern[i]==pattern[k]) {
            next[i]=k+1;
            k=next[i];
            i++;
        }
        else if(k==0) next[i++]=0;
        else k=next[k-1];
    }

算出 next数组后,剩下的就是kmp算法了。

用i指针指向主串,j指针指向模式串。
kmp算法的i指针只会向右移动。
比较main[i] 和 pattern[j]。如果他们相等。那他们都向前移动一格。
如果不相等,则让j回溯到next[j-1]。这个步骤的图如下
在这里插入图片描述
这时再比较i和j。如果还不相等。最让j再回溯到next[j-1]。
直到main[i] =pattern[j]。或者 j ==0。
如果j == 0,那就让i向前移动,表示i指针当前位置无法与模式串匹配。代码如下:

for(int i=0,j=0;i<main.length();){
        if(main[i]==pattern[j]){
            i++;j++;
            if(j==pattern.size()) return i-j;
        }
        else if(j>0) j=next[j-1];
        else i++;
    }

总体代码如下:

#include <string>
#include <vector>
#include <iostream>
using namespace std;
int kmp(string main,string pattern){
    if(pattern.size()>main.size()){
        return -1;
    }
    vector<int> next(pattern.size(),0);
    next[0]=0;
    int k=next[0];
    for(int i=1;i<pattern.size();){
        if(pattern[i]==pattern[k]) {
            next[i]=k+1;
            k=next[i];
            i++;
        }
        else if(k==0) next[i++]=0;
        else k=next[k-1];
    }
    cout <<endl;
    for(int i=0;i<next.size();i++){
        cout << next[i]<<" ";
    }
    cout<<endl;
    for(int i=0,j=0;i<main.length();){
        if(main[i]==pattern[j]){
            i++;j++;
            if(j==pattern.size()) return i-j;
        }
        else if(j>0) j=next[j-1];
        else i++;
    }
    return -1;

}
int main(){
    string mainString = "adbcafabcadcabfgabcabfgabcabcabfabcdabdadva";
    string pattern = "abcabfgabcabcabf";
    cout << kmp(mainString,pattern)<<endl;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值