zoukankan      html  css  js  c++  java
  • KMP学习总结

    初学,理解可能不是那么准确~~
    Next数组的含义:next[i]表示第0个元素到第i个元素组成的字符串的最大前缀后缀。Next[0]=0显然。
    所以KMP的原理就是
    这里写图片描述
    通过找出每一阶段最大的相等的前缀后缀,那么匹配到某个字符失配时就可以从前缀的下一个字母开始匹配,而不用再回退匹配。使复杂度降从O(m^n)到O(m+n)。
    最重要的就是求next数组,而且next数组在很多题都有应用,不止KMP。
    而得到next数组的原理如下:
    这里写图片描述
    (画风就是这么洒脱,没办法~~~)
    色的如图,如果比较到p,如果p点元素和q点元素相等,那么直接S1的长度加1就是next[q]了,即next[q]=next[q-1]+1。 

    但是如果不等,就要在S1中找到一个短一点的字符串,如棕色的那段,那样,因为黄色的那段S2和S1是相等的,那么在S1中找到的最长前缀后缀也和S2的后缀相等,这时,如果A点那里和q相等,那么next[q]就是棕那段长度+1喽~如果不相等,再向前减,对,这个过程就是代码里那里【k=next[k];】。如果到紫色的那里B和q相同,那么next[q]为紫色的长度+1.如果一直不相等,只能是0喽~
    代码如下(代码原网址链接):

    void makeNext(const char P[],int next[])
    {
        int q,k;//q:模版字符串下标;k:最大前后缀长度
        int m = strlen(P);//模版字符串长度
        next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
        for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
        {
            while(k > 0 && P[q] != P[k])//递归的求出P[0]•••P[q]的最大的相同的前后缀长度k
                k = next[k-1];          
            if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
            {
                k++;
            }
            next[q] = k;
        }
    }

    但很多时候next数组的意义是:next[i]表示第0个元素到第i-1个元素组成的字符串的前缀后缀,也就是如果把上面讨论的数组定义为nexta的话,那么nexta[i]=next[i+1] , 即nexta[0]=next[1]……nexta[n-1]=next[n],并定义next[0]=-1。
    代码如下:

    void GetNext()
    {
        //这个代码的原理和上差不多,就是改的短了些
        int j = 0, k = -1;
        Next[0] = -1;
        while (j <= tlen)
        {
            if (k == -1 || T[j] == T[k])
            {
                Next[++j] = ++k;
            }
            else
                k = Next[k];
        }
    }
    
    int KMP_Index()
    {
        int i = 0, j = 0;
        GetNext();
        while (i < slen && j < tlen)
        {
            if (j == -1 || S[i] == T[j])
            {
                i++;
                j++;
            }
            else
                j = Next[j];
        }
        if (j == tlen)
            return i - tlen;
        return -1;
    }
    

      

    也可以求一个字串出现的次数,代码稍作改动即可:

    int KMP_Count()
    {
        int ans = 0;
        int i = 0, j = 0;
        GetNext();
        while (i != slen && j != tlen)
        {
            if (S[i] == T[j] || j == -1)
                ++i, ++j;   //第一次发现还可以这样写~
            else
                j = Next[j];
            if (j == tlen)
            {
                ++ans;
                j = Next[j];
            }
        }
        return ans;
    }
    

      

    举例说明理解一下

    对于tytytyty

    tytytyty                // ↓子串    // ↓前缀后缀
    next[0] = -1
    next[1] = 0		// t		
    next[2] = 0		// ty
    next[3] = 1		// tyt      // t
    next[4] = 2		// tyty     // ty 
    next[5] = 3		// tytyt    // tyt
    next[6] = 4		// tytyty   // tyty
    next[7] = 5		// tytytyt  // tytyt
    next[8] = 6		// tytytyty // tytyty
    

      

    KMP入门题

    hdu2087 AC代码:

    /*******************************************************
    Problem : 2087 ( 剪花布条 )     Judge Status : Accepted
    RunId : 14705531    Language : G++    Author : G_lory
    Code Render Status : Rendered By HDOJ G++ Code Render Version 0.01 Beta
    *******************************************************/
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 1005;
    
    char s[N];        // 原字符串
    char t[N];        // 匹配子串
    char nt[N];        // nt[i]表示t第0~i-1个元素组成的字符串的前缀后缀
    
    int slen, tlen;    // 字符串 s, t 的长度
    
    void get_next()
    {
        int j = 0, k = -1;
        nt[0] = -1;
        while (j <= tlen)
        {
            if (k == -1 || t[j] == t[k])
            {
                nt[++j] = ++k;
            }
            else
                k = nt[k];
        }
    }
    
    int kmp_cnt()
    {
        int ans = 0;
        int i = 0, j = 0;
        get_next();
        while (i != slen && j != tlen)
        {
            if (s[i] == t[j] || j == -1)
                ++i, ++j;
            else
                j = nt[j];
            if (j == tlen)
            {
                ++ans;
                //j = nt[j];
                j = 0;
            }
        }
        return ans;
    }
    
    int main()
    {
        while (scanf("%s", s) != EOF) {
            slen = strlen(s);
            if (slen == 1 && s[0] == '#') break;
            scanf("%s", t);
            tlen = strlen(t);
            printf("%d
    ", kmp_cnt());
        }
        return 0;
    }
    

      

    hdu1711 AC代码:

    /***********************************************************
    Problem : 1711 ( Number Sequence )     Judge Status : Accepted
    RunId : 14705762    Language : G++    Author : G_lory
    Code Render Status : Rendered By HDOJ G++ Code Render Version 0.01 Beta
    ************************************************************/
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    int ss[1000005];
    int tt[10005];
    int nt[10005];
    int slen, tlen;
    
    void get_next()
    {
        int j = 0, k = -1;
        nt[0] = -1;
        while (j <= tlen)
        {
            if (k == -1 || tt[j] == tt[k])
            {
                nt[++j] = ++k;
            }
            else
                k = nt[k];
        }
    }
    
    int kmp_idx()
    {
        int i = 0, j = 0;
        get_next();
        while (i < slen && j < tlen)
        {
            if (j == -1 || ss[i] == tt[j]) ++i, ++j;
            else j = nt[j];
        }
        if (j == tlen)
            return i - tlen;
        return -1;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d", &slen, &tlen);
            for (int i = 0; i < slen; ++i) scanf("%d", ss + i);
            for (int i = 0; i < tlen; ++i) scanf("%d", tt + i);
            int ans = kmp_idx();
            printf("%d
    ", ans == -1 ? -1 : ans + 1);
        }
        return 0;
    }
    

      

  • 相关阅读:
    sslforfree的证书合并成类似于certbot的ssl证书文件
    190129 胡思乱想
    Android deprecated apache module (HttpClient, HttpResponse, etc.)
    黑阀 adb 命令
    windows10 vs2019 + opencv 3.4.7环境搭建
    ASP.NET MVC 微信公众号支付,微信公众平台配置
    jQuery 滚动条 滚动到底部(下拉到底部) 加载数据(触发事件、处理逻辑)、分页加载数据
    js显示yyyy年mm日dd天 星期几 的格式日期
    jQuery对 动态添加 的元素 绑定事件(on()的用法)
    Jquery判断页面图片是否加载失败,加载失败则显示默认图片
  • 原文地址:https://www.cnblogs.com/wenruo/p/4492691.html
Copyright © 2011-2022 走看看