【説明する】KMP

KMP是一个困扰我很久的算法,听老师或者是学姐讲了差不多有4次了,但是还是搞不太懂,今天终于,终于,终于搞懂了!

                          ——2017-10-29 Vanora

首先推荐一下KMP详解——July

读罢之后内心只有一个感觉:我的KMP终于可以毕业了qwq

学东西千万不要求快!细细地,慢慢地去读这篇文章,相信你也可以从头到尾彻底理解KMP算法呦~

接下来是一些KMP的练手题:

做完这些并且真正搞懂之后,相信你一定就会KMP算法了~(一定要理解了,吃透了!)

1.P3375 【模板】KMP字符串匹配

直通车

思路:

  真正意义上的KMP板子题,很良心(不加优化版)

新知识Get!!!:

  char数组不清零,直接用cin读入的话,自动清零!!!好厉害的样子!!!

坑点:

  一定要真正理解nxt[]求得到底是什么,是用匹配串求解还是用文本串

上代码:

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

const int M = 1000011;
char s[M],p[M];
int lens,lenp;

int nxt[M];
void getnxt() {
    int k=-1,j=0;
    nxt[0]=-1;
    while(j<lenp) {
        if(k==-1 || p[j]==p[k]) {
            j++;k++;
            nxt[j]=k; //记录更新的nxt值 
        }
        else k=nxt[k];
    }
}

void KMP() {
    int i=0,j=0;
    while(i<lens) {
        if(j==-1 || s[i]==p[j]) i++,j++; 
        //j==-1:没有nxt,所以从一开始进行匹配 
        //s[i]==p[j]:当前文本串与匹配串的位置上的字符匹配成功,继续匹配 
        else j=nxt[j]; //寻找更短的公共前后缀 
        if(j==lenp) {
            printf("%d
",i-lenp+1);
            i--; //文本串之前已经匹配过了,所以没有再次进行匹配的需要了 
            j=0; //匹配串从新开始进行匹配 
        }
    }
} 

int main() {
    cin>>s>>p;
    lens=strlen(s);
    lenp=strlen(p);
    getnxt();
    KMP();
    for(int i=1; i<=lenp; i++) printf("%d ",nxt[i]);
    return 0;
}
View Code

2.POJ  3461  Oulipo

直通车

思路:

  用KMP来做,如果成功匹配,那么ans++,最后输出ans即可

坑点:

  ①注意他的输入顺序。。。。。。
  ②注意是多组数据输入。。。。。
  ③该题用上述优化过的方法都过不了。。。。
  ④该题数据范围一定要看好了。。。
  ⑤W,T范围是不同的。。
  ⑥最好不要用gets读入。

上代码:

1)while版(mine)

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

const int M = 1000011;
const int N = 10011;
char s[M],p[N];
int T,ans,lens,lenp;

int nxt[N];
void getnxt() {
    int k=0,j=1;
    nxt[0]=-1;nxt[1]=0;
    while(j<lenp) {
        if(k==-1 || p[j]==p[k]) {
            k++,j++;
            nxt[j]=k;
        }
        else k=nxt[k];
    }
}

void KMP() {
    int i=0,j=0;
    while(i<lens) {
        if(s[i]==p[j]) i++,j++;
        else if(j>=0) j=nxt[j];
        else i++,j=0;
        if(j==lenp) ans++,j=nxt[j];
    }
} 

int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%s%s",p,s);
        lens=strlen(s);
        lenp=strlen(p);
        getnxt();
        KMP();
        printf("%d
",ans);
        ans=0;
    }
    return 0;
}
View Code

2)从网上学到的另外一种方法,感觉十分类似,然而能AC。。。服气。。。

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

const int M = 1000011;
const int N = 10011;
char s[M],p[N];
int ans,lens,lenp;

int nxt[N];
void getnxt() {
    int k=0,j;
    nxt[0]=-1;nxt[1]=0;
    for(j=2; j<=lenp; j++) {
        while(k>=0 && p[k]!=p[j-1]) k=nxt[k];
        nxt[j]=++k;
    }
}

void KMP() {
    int i=0,j=0;
    while(i<lens) {
        if(s[i]==p[j]) i++,j++; 
        else if(j>=0) j=nxt[j];
        else i++,j=0;
        if(j==lenp) ans++,j=nxt[j];
    }
} 

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%s%s",p,s);
        lens=strlen(s);
        lenp=strlen(p);
        getnxt();
        KMP();
        printf("%d
",ans);
        ans=0;
    }
    return 0;
}
View Code

3)采用for循环的一种方法

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

const int M = 1000001;
const int N = 10001;
int T,ans,lens,lenp;
char s[M],p[N];

int nxt[N];
void getnxt() {
    nxt[1]=0;
    for(int i=1; i<lenp; i++) {
        int j=nxt[i];
        while(j && p[i]!=p[j]) j=nxt[j];
        nxt[i+1] = p[i]==p[j] ? j+1 : 0;
    }
}

void KMP() {
    int j=0;
    for(int i=0; i<lens; i++) {
        while(j && (p[j]!=s[i])) j=nxt[j];
        if(p[j]==s[i]) j++;
        if(j==lenp) ans++;
    }
}

int main() {
    scanf("%d",&T);
    while(T--) {
        memset(nxt,0,sizeof(nxt));
        ans=0;
        scanf("%s%s",p,s);
        lenp=strlen(p);
        lens=strlen(s);
        getnxt();
        KMP();
        printf("%d
",ans);
    }
    return 0;
}
View Code

3.P1308 统计单词数

直通车

思路:

  ①因为会出现空格,所以手动将s,p的左右均+' ',然后再进行KMP即可

  ②不需要+' ',直接在KMP匹配成功之后判断是否s左右均拥有' '即可

      不过需要注意的是:当在一开头就匹配成功的时候则只需要判断是否s右边有' '即可

坑点:

  千万不要用gets进行输入,会编译错误。。。。

上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;

const int pl = 15;
int lens,lenp,ans=-1,firsta=-1;
string s,p;

int nxt[pl];
void getnxt() {
    int k=0,j=1;
    nxt[0]=-1,nxt[1]=0;
    while(j<lenp) {
        if(k==-1 || p[k]==p[j]) {
            k++,j++;
            nxt[j]=k;
        }
        else k=nxt[k];
    }
}

void KMP() {
    int i=0,j=0;
    while(i<lens) {
        if(s[i]==p[j]) i++,j++;
        else if(j>=0) j=nxt[j];
        else i++,j=0;
        if(j==lenp) {
            if(firsta==-1) firsta=i-j;
            ans++,j=nxt[j];
        }
    }
}

void tochange(string &a,int len) {
    for(int i=0; i<len; i++) {
        if(a[i]==' ') continue;
        int x=a[i]-'A';
        if(x>=32) x-=32;
        char y=x+'a';
        a[i]=y;
    }
}

int main() {
    getline(cin,p);
    p=' '+p+' ';
    lenp=p.length();
    tochange(p,lenp);
    getline(cin,s);
    s=' '+s+' ';
    lens=s.length();
    tochange(s,lens);
    getnxt();
    KMP();
    if(ans==-1) printf("%d",ans);
    else printf("%d %d",ans+1,firsta);
    return 0;
}
View Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;

const int pl = 15;
int lens,lenp,ans=-1,firsta=-1;
string s,p;

int nxt[pl];
void getnxt() {
    int k=0,j=1;
    nxt[0]=-1,nxt[1]=0;
    while(j<lenp) {
        if(k==-1 || p[k]==p[j]) {
            k++,j++;
            nxt[j]=k;
        }
        else k=nxt[k];
    }
}

void KMP() {
    int i=0,j=0;
    while(i<lens) {
        if(s[i]==p[j]) i++,j++;
        else if(j>=0) j=nxt[j];
        else i++,j=0;
        if(j==lenp && s[i]==' ') {
            if(i>j && s[i-j-1]==' ') {
                if(firsta==-1) firsta=i-j;
                ans++,j=nxt[j];
            } else {
                if(i==j) {
//              if(i==0) { //等价 
                    if(firsta==-1) firsta=i-j;
                    ans++,j=nxt[j];
                }
            }
        }
    }
}

void tochange(string &a,int len) {
    for(int i=0; i<len; i++) {
        if(a[i]==' ') continue;
        int x=a[i]-'A';
        if(x>=32) x-=32;
        char y=x+'a';
        a[i]=y;
    }
}

int main() {
    getline(cin,p);
    lenp=p.length();
    tochange(p,lenp);
    getline(cin,s);
    lens=s.length();
    tochange(s,lens);
    getnxt();
    KMP();
    if(ans==-1) printf("%d",ans);
    else printf("%d %d",ans+1,firsta);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7750431.html