zoukankan      html  css  js  c++  java
  • 【説明する】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
  • 相关阅读:
    DOM2DOM3续
    总结 @ 在 C# 中的用法 (装载)
    ORACLE10G卸载过程
    .net中访问oracle数据库的几种方式(转载)
    试图运行项目时出错,无法启动调试。没有正确安装调试器。请运行安装程序安装或修复调试器
    设计模式 构造器
    设计模式 抽象工厂
    linq中日期格式转换或者比较,程序报错说不支持方法的解决办法
    bootstrap图标字体不出来问题的解决办法
    JavaScript对象属性访问的两种方式
  • 原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7750431.html
Copyright © 2011-2022 走看看