zoukankan      html  css  js  c++  java
  • 从零理解的KMP算法

    KMP算法的优势

    KMP算法是一个效率很高的字符串匹配算法,算法大意是:给定两个字符串y,x,判断x是否在y出现过。如果暴力搜索的话复杂度为O(lenx*leny),但用KMP算法解决的话,

    我们只需要一个O(lenx)的预处理,优化暴力的复杂度变成O(lenx+leny),这里lenx,leny都代表字符串的长度。

    KMP算法的匹配方法

    给定一个y为:ababacddsdfwwrababsababa

    给定一个x为:ababa

    我们先用暴力的方法跑一遍

    1.

    ababacddsdfwwrababsababa

    ababa

    这里我们可以看到指针到了第一个字符时候,x和y在此指针上的字符相同;

    2.

    ababacddsdfwwrababsababa

    ababa

      ↑

    继续让指针向后,我们发现此时x和y的字符仍相同,那么继续

    当我们跑下去会发现第一个y中含有x的字符串,继续往下跑时会发现第一个字符就不相同,那就继续往下,直到找到第一个相同的字符

    3.

    ababacddsdfwwrababsababa

        ababa

              ↑

    这个时候我们重复第一步,发现找到了最后一个c和b不相同,那么我们就会放弃这次查找,又从头开始匹配;

    4.

    ababacddsdfwwrababsababa

          ababa

          ↑

    显然这样效率太低了,那有没有办法提高效率呢???

    答案当然是有的,我们可以发现在第三步的时候x的aba与y的aba是相同的,如果这一段两个字符串没法匹配,说明y中的aba一段一定不可以再匹配了,我们就可以跳过aba直接进行下一段搜索如下图

    ababacddsdfwwrababsababa

              ababa

             

    接下来如何判断就是KMP干的事情了

    我们可以想到如果计算出来x字符串的一个转移函数next就会轻松很多。

    next数组的含义就是一个字符串的其中一个字符之前的最长前缀和最长后缀相同的长度。

    举个例子

    abababa  最长前缀和最长后缀都是ababa

    abebc       就没有最长前缀和最长后缀

     qqqq       最长前缀和最长后缀就是qqq(注意不是qqqq

    那么对于咱们的字符串ababa我们就可以做出处理

    分别计算与a,ab,aba,abab,ababa的长度相同的最长前缀和最长后缀长度(如果下面不理解就仔细看上面的例子。

    next[0]=-1,next[1]=-1,next[2]=0,next[3]=1,next[4]=2

    -1表示不存在,0表示长度为1,1表示长度为2,以此类推(继续看就会知道为什么这么存

    那么根据我上面说的要想把那些多余的搜索跳过就要使用这个数组,

    想必大家都已经明白了

    当我们搜到一个字符匹配,此时它对应的next有一个值,当他下一个字符不匹配时,可以直接移动next的值+1个长度,继续比较。

    ababacddsdfwwrababsababa

                               ababa

                                       ↑

    ababacddsdfwwrababsababa

                                  ababa

                                                      

    洛谷上题号P3375是这道题的模板,大家可以去练习一下

    下面是我写的代码(已订正错误

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int o=0,a[10000001];
    int next1[10000001],count=0;
    char x[10000001],y[10000001];
    void cal_next(int len)
    {
        next1[0]=-1;
        int k=-1,q;
        for(q=1;q<=len-1;q++)
        {
            while(k>-1&&x[k+1]!=x[q])
            {
                k=next1[k];
            }
            if(x[k+1]==x[q])
            {
                k=k+1;
            }
            next1[q]=k;
        }
    }
    void KmpSearch()  
    {    
        int i=0;  
        int j=-1;  
        int sLen=strlen(y);  
        int pLen=strlen(x);  
        while(i<sLen)  
        {
            while(j>-1&&x[j+1]!=y[i])
            j=next1[j];
            if(x[j+1]==y[i])
            {
                j++;
            }
            if(j==pLen-1&&y[i]==x[j])
            {
                count++;
                a[o]=i-j;
                j=-1;
                i=i-pLen+2;
                o++;
                j=0;
            }
            i++;
        }   
    }  
    int main()
    {
        int n;
        int i;
        scanf("%s",y);
        scanf("%s",x);
        int lenth=strlen(x);
        cal_next(lenth);
        KmpSearch();
        for(i=0;i<o;i++)
        {
            printf("%d
    ",a[i]+1);
        }
        for(i=0;i<lenth;i++)
        {
            printf("%d ",next1[i]+1);
        } 
    }
    View Code
  • 相关阅读:
    git track
    npm 升级到最新版本
    三行代码实现垂直居中和cube
    布局之定位
    MongoDB的安装问题
    正则表达式
    javascript表单验证
    Oracle中创建表,行级触发器,序列
    查找某个字符在字符串中出现的次数
    oracle表中有一列id她是自动增长的,插入一条数据时怎么取得id的值
  • 原文地址:https://www.cnblogs.com/ashon37w/p/7089225.html
Copyright © 2011-2022 走看看