KMP算法详解

问题:已知两个字符串分别为S、P如下图,判断s串中存在几个p串,并输出其起始位置?

目录

         两种算法的区别 

         暴力解决

         KMP算法

       


两种算法的区别:

黄线为已经匹配的部分,两段绿线为c之前相同的前后缀,当c失配时,分别从箭头位置开始匹配的效果是完全相同的。

暴力是从第一个箭头开始,KMP是从第二个开始,这就是两中方法的差别。(不知道能不能看懂)

一、暴力解决:

复杂度:O(N*M

1、abab匹配成功,但当 i=4 (j=4) 时d与c失配。

2、令j=0,i=1重新开始匹配,当i=1(j=0)时失配,继续移动。

3、重复上面的步骤,知道最后匹配成功,输出起始位置。

代码模板:

void Search(string a,string p)
{
    int la=s.size(),lp=p.size();
    for(int i=0;i<la;i++){
        for(int j=0;j<lp;j++){
            if(a[i]!=p[j]){
                break;
            }
            if(j==lp-1){
                cout<<i<<endl;
            }
        }
    }
}

上面的复杂度确实很高,KMP算法的优点就是用线形的时间复杂度解决这个问题。

二、KMP算法

复杂度:O(N+M) 

1、同样abab匹配成功,但当 i=4 (j=4) 时d与c失配。

2、此时令j=Next[j],即j=Next[4]=2,i不变,继续比较,此时d与a失配。(先不要关心Next[4]=2什么意思,求Next数组传送门

3、重复上面的步骤,直到最后完全匹配为止。

比较上面的两种方法应该能看出复杂度的曲别。

Next数组含义:

以p串为例子,它的每个坐标对应的Next数组的值如下:

Next数组的含义:Next[4]=2的含义就是 [0,3] 这个子串的前缀与后缀最大相同的字符数目为2。

求解Next数组:

先上代码(找了很多天,这个算是最好用的了)      感谢:Venishel

///p[k]为前缀,p[i]为后缀
void getNext(string p,LL lp,LL Next[]){
    LL k=0;
    for(LL i=1;i<lp;i++){
        while(k&&p[i]!=p[k]) k=Next[k];
        if(p[i]==p[k]) k++;
        Next[i+1]=k;
    }
}

KMP算法匹配:

感谢: v_JULY_v

该字符对应的Next 值会告诉你下一步匹配中,j应该跳到哪个位置(跳到Next [j] 的位置)。如果Next [j] 等于0,则跳到p串的开头,若Next [j] 不为0,代表下次匹配跳到Next[j]的位置,而不是跳到开头,且具体跳过了j-Next[j] 个字符。

像下面的这种情况:

1、现在d与c失配:

2、Next[j]=2,即j跳到2的位置。

代码: 

void kmp(string a,string p,LL la,LL lp){
    LL j=0;
    for(LL i=0;i<la;i++){
        while(j&&a[i]!=p[j]) j=Next[j];
        if(a[i]==p[j]) j++;
        if(j==lp) cnt++,j=0;
    }
}

KMP完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAX=1e6;
LL Next[MAX+5],cnt;
void getNext(string p,LL lp,LL Next[]){
    LL k=0;
    for(LL i=1;i<lp;i++){
        while(k&&p[i]!=p[k]) k=Next[k];
        if(p[i]==p[k]) k++;
        Next[i+1]=k;
    }
}
void kmp(string a,string p,LL la,LL lp){
    LL j=0;
    for(LL i=0;i<la;i++){
        while(j&&a[i]!=p[j]) j=Next[j];
        if(a[i]==p[j]) j++;
        if(j==lp) cnt++,j=0;
    }
}
int main()
{
    string a,p;
    cin>>a>>p;
    LL la=a.size(),lp=p.size();
    getNext(p,lp,Next);
    kmp(a,p,la,lp);
    cout<<cnt<<endl;
    return 0;
}

 

原文地址:https://www.cnblogs.com/ldu-xingjiahui/p/12407411.html