zoukankan      html  css  js  c++  java
  • 扩展KMP算法小记

    参考来自《拓展kmp算法总结》:http://blog.csdn.net/dyx404514/article/details/41831947

    扩展KMP解决的问题:

    定义母串S和子串T,S的长度为n,T的长度为m;

    求  字符串T  与  字符串S的每一个后缀  的最长公共前缀;

    也就是说,设有extend数组:extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i<n)。

    (注意到,如果存在若干个extend[i]=m,则表示T在S中完全出现,且是在位置i出现,这就是标准的KMP问题,所以一般将它称为扩展KMP算法。)

    下面举一个例子,S=”aaaabaa”,T=”aaaaa”;

    首先,计算extend[0]时,需要进行5次匹配,直到发生失配:从而得知extend[0]=4;

    下面计算extend[1],在计算extend[1]时,是否还需要像计算extend[0]时从头开始匹配呢?

    答案是否定的,因为通过计算extend[0]=4,从而可以得出S[0,3]=T[0,3],进一步可以得到 S[1,3]=T[1,3];

    计算extend[1]时,事实上是从S[1]开始匹配;

    设有辅助数组next[i]表示T[i,m-1]和T的最长公共前缀长度,

    在这个例子中,next[1]=4,即T[0,3] = T[1,4],进一步得到T[1,3]=T[0,2],所以S[1,3]=T[0,2],所以在计算extend[1]时,通过extend[0]的计算,已经知道S[1,3]=T[0,2];

    所以前面3个字符已经不需要匹配,直接匹配S[4]和T[3]即可,这时一次就发生失配,所以extend[1]=3.

    1. 拓展kmp算法一般步骤

    通过上面的例子,事实上已经体现了拓展kmp算法的思想,下面来描述拓展kmp算法的一般步骤。

    首先我们从左到右依次计算extend数组,在某一时刻,设extend[0...k]已经计算完毕,并且之前匹配过程中所达到的最远位置为P,所谓最远位置,严格来说就是i+extend[i]-1的最大值(0<=i<=k);

    设取到这个最大值的位置为Po,如在上面的例子中,计算extend[1]时,P=3,Po=0.

    现在要计算extend[k+1],根据extend数组的定义,可以推断出S[Po,P]=T[0,P-Po],从而得到 S[k+1,P]=T[k-Po+1,P-Po],令len=next[k-Po+1](next[i]表示T[i,m-1]和T的最长公共前缀长度);

    分以下两种情况讨论:

    第一种情况:k+len<P

    如下图所示:

    上图中,S[k+1,k+len]=T[0,len-1],

    然后S[k+len+1]一定不等于T[len],因为如果它们相等,则有S[k+1,k+len+1]=T[k+po+1,k+Po+len+1]=T[0,len],那么next[k+Po+1]=len+1,这和next数组的定义不符;

    所以在这种情况下,不用进行任何匹配,就知道extend[k+1] = len.

    一个例子的模拟:

      

    第二种情况: k+len>=P

    如下图:

    上图中,S[P+1]之后的字符都是未知的,也就是还未进行过匹配的字符串,所以在这种情况下,就要从S[P+1]和T[P-k+1]开始一一匹配,直到发生失配为止,当匹配完成后,如果得到的extend[k+1]+(k+1)大于P则要更新未知P和Po.

    另一个例子的模拟:

    至此,拓展kmp算法的过程已经描述完成,细心地读者可能会发现,next数组是如何计算还没有进行说明;

    事实上,计算next数组的过程和计算extend[i]的过程完全一样,将它看成是以T为母串,T为子串的特殊的拓展kmp算法匹配就可以了;

    计算过程中的所需要的next数组值全是已经计算过的,所以按照上述介绍的算法计算next数组不会出现问题,不再赘述。

    代码模板:

    const int MAX=100010; //字符串长度最大值
    int Next[MAX],extend[MAX];
    
    //预处理计算Next数组
    void getNext(char str[])
    {
        int i=0,j,po,len=strlen(str);
        next[0]=len; //初始化next[0]
        while(str[i]==str[i+1] && i+1<len) i++; next[1]=i; //计算next[1]
        po=1; //初始化po的位置
        for(i=2;i<len;i++)
        {
            if(next[i-po]+i < next[po]+po) //第一种情况,可以直接得到next[i]的值
                next[i]=next[i-po];
            else //第二种情况,要继续匹配才能得到next[i]的值
            {
                j = next[po]+po-i;
                if(j<0) j=0; //如果i>po+next[po],则要从头开始匹配
                while(i+j<len && str[j]==str[j+i]) j++; next[i]=j;
                po=i; //更新po的位置
            }
        }
    }
    
    //计算extend数组
    void EXKMP(char s1[],char s2[])
    {
        int i=0,j,po,len=strlen(s1),l2=strlen(s2);
        getNext(s2); //计算子串的next数组
        while(s1[i]==s2[i] && i<l2 && i<len) i++; extend[0]=i;
        po=0; //初始化po的位置
        for(i=1;i<len;i++)
        {
            if(next[i-po]+i < extend[po]+po) //第一种情况,直接可以得到extend[i]的值
                ex[i]=next[i-po];
            else //第二种情况,要继续匹配才能得到extend[i]的值
            {
                j = extend[po]+po-i;
                if(j<0) j=0; //如果i>extend[po]+po则要从头开始匹配
                while(i+j<len && j<l2 && s1[j+i]==s2[j]) j++; extend[i]=j;
                po=i; //更新po的位置
            }
        }
    }    
  • 相关阅读:
    Mybatis自动生成Xml文件,针对字段类型为text等会默认产生XXXXWithBlobs的方法问题
    java JDK JRE 1.6,1.7,1.8各个版本版本下载链接
    window 10 企业版激活
    IntelliJ IDEA 缺少 javax 包 支持
    IntelliJ Idea 2017 免费激活方法
    url的三个js编码函数escape(),encodeURI(),encodeURIComponent()简介【转】
    【转载】 CUDA_DEVICE_ORDER 环境变量说明 ( ---------- tensorflow环境下的应用 )
    【转载】 TensorFlow tf.app&tf.app.flags用法介绍
    中国知网(cnki)上caj格式转pdf的方法 ----------------- 转载
    同时购入两台同款thinkpad笔记本电脑,分别使用同一账户激活office失败--------------解决方法(账户下有多个Office激活信息,重装后提示“许可证不正确或者最大激活次数”)
  • 原文地址:https://www.cnblogs.com/dilthey/p/8620119.html
Copyright © 2011-2022 走看看