KMP算法小记

主函数:
负责子字符串匹配主字符串的功能
next计算函数:
负责计算子字符串的next数组,next用于确定在字符匹配过程中遇到不匹配字符时子字符串指针应该回退到什么位置。
例如:
主字符串:abcab d efg
子字符串:abcab c
很明显,第六个字符处不匹配,这时候子字符串该如何回退匹配指针重新匹配呢?正确答案是:
主字符串:abcab d efg
子字符串: ab c abc
这看起来可能有点别扭,因为既然第六个字符c无法匹配,那第三个字符也是c,必然也会无法匹配,为什么会如此回退呢?这是由于,在计算回退数组next的时候,都是假设下一个字符为未知的情况下,计算好下一个字符如果不匹配将会回退到哪个位置的。也就是说在计算next的时候,在第五个位置b上,就已经计算好了第六个位置c不匹配将回退到哪里。这么计算的原因在于,计算next数组时本身就需要用当前的字符与之前的字符进行匹配,如果不匹配的话本身就需要回退,如果不知道当前字符不匹配时如何回退,也就无法继续计算下去。所以每次计算都是计算下一个字符如果不匹配,那么将回退到哪里。
计算next的代码逻辑如下:

void getNext(vector<int> &next,string &s){
    next[0]=-1;
    int strlen=s.size();
    int i=0,j=-1;//此处的i类似于主字符串指针,j类似于子字符串指针
    while(i<strlen){
        if(j==-1||s[i]==s[j]){
            next[++i]=++j;//每次计算的都是下一个字符不匹配时应该回退的位置
        }
        else{
            j=next[j];//每次当前字符和之前字符不匹配时,则j回退
        }
    }
}

主函数如下:

int KMP(string s1,string s2)
{
    int strlen1=s1.length();
    int strlen2=s2.length();
    int pos1=0,pos2=0;
    vector<int> next(strlen2+1,0);
    getNext(next,s2);
    while(pos1<strlen1&&pos2<strlen2){//如果pos2==strlen2,说明已经匹配成功了,退出;如果pos1==strlen1,说明主字符串剩余部分长度已经小于子字符串了,在主串中找不到可匹配子串,退出。
        if(pos2==-1||s1[pos1]==s2[pos2]){//如果pos2==-1,说明当前子字符串字符与主字符串字符正在匹配的字符前面毫无重叠部分,且当前字符也已经未匹配成功,只能开始在下一点开始重新匹配
            pos1++;
            pos2++;
        }else{
            pos2=next[pos2];
        }
    }
    if(pos2==strlen2){
        return pos1-pos2;
    }
    return -1;
}
posted @ 2021-09-22 11:31  一只小菜菜鸟  阅读(45)  评论(0)    收藏  举报