拓展KMP算法详解

算法描述:设字符串T,长度为n,字符串S,长度为m。在线性时间内求出T的每一个后缀所对应S的最长前缀。

假设T=“AAAAB”,S=”AAAA”。

我们从第一位开始匹配,匹配结果存放到ext数组中。显然ext【0】=4,然后我们就去计算ext【1】,显然ext【1】=3。如果还是如同算ext【0】这样的方法去匹配ext【1】会造成信息的浪费,因为前面有的信息已经知道了,就不需要一个一个再去比对了。这里我们记S【1到lens-1的元素】为S1,发现S1与S是有公共前缀的,同样我们用next数组记录下这公共前缀,显然next【1】=3。所以从T【1】开始之后的next【1】(3)位的数字都是相同的,这样就不用从头开始比对。

下面用图来举一个新的栗子。

我用黑色方格代表已经匹配的元素,黑色单向箭头代表元素相等。红色箭头代表待匹配元素。

当我们匹配完第一个元素(下标0)的时候开始匹配第二个元素(下标1),发现ext【1】=2,这样的话可以直接从S串的2号元素开始匹配,原因正如之前所说。

《拓展KMP算法详解》

基本原理就是这个样子。下面说一说具体如何实现拓展kmp算法

首先我们要引入两个新的变量,p,p0。表示p从p0开始最长的匹配位置(如果感到模糊请继续看下去)。与manacher类似,我们需要维护这个最长长度。下面通过几个图来讲解一下。

(1)如图所示,红色箭头代表p,蓝色箭头代表p0,绿色箭头代表待匹配位置,设next【绿色箭头】=3。然后就有了下面的匹配方式。由于前三个相同,我们直接匹配第四个,也就是绿色方格。但是绿色方格中的元素可能相等吗?答案是否定的。如果相等了,我们的next【绿色箭头】就不是3了,而是4。虽然方格中没有真正的元素,但是仍然可以推的出绿色方格中的元素是不可能相同的。

《拓展KMP算法详解》

绿色部分如果匹配了,那么next【绿色箭头】就产生了矛盾,S中4,5号元素就会相等

《拓展KMP算法详解》

这是绿色箭头指向的元素匹配位置小于蓝色箭头(p0)的情况,那么大于等于的情况会是怎么样呢?

(2)紧接着上一个栗子,如果next【绿色箭头】=4,绿色箭头+next【绿色箭头】刚刚好指向了蓝色箭头的位置,这时候应该向下一位置开始匹配,因为还没有搜索到那个位置,所以你并不知道是不是会匹配,如果匹配了,那么就更新p与p0,如此反复,ext数组就求解完毕,算法结束。

《拓展KMP算法详解》.

然后我去抄了一个模板

const int maxn=10086;   //字符串长度最大值
int next[maxn],ex[maxn]; //ex数组即为extend数组
//预处理计算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)//计算next[1]
        i++;
    next[1]=i;
    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])//计算next[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)//计算ex[0]
        i++;
    ex[0]=i;
    po=0;//初始化po的位置
    for(i=1; i<len; i++)
    {
        if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
            ex[i]=next[i-po];
        else//第二种情况,要继续匹配才能得到ex[i]的值
        {
            j=ex[po]+po-i;
            if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
            while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
                j++;
            ex[i]=j;
            po=i;//更新po的位置
        }
    }
}
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/ycq_lancet/article/details/77466879
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞