您的位置:首页 > 其它

从头到尾彻底理解扩展KMP

2015-05-16 09:01 381 查看

1. 扩展KMP问题:

求字符串S的所有后缀和字符串T的最长公共前缀。


扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。

2. 拓展KMP的next[]数组怎么计算?

在解上面这个问题前我们要先解决一个类似的问题:

字符串s所有后缀字符串s本身的最长公共前缀;

我们用next[]next[]数组保存这些值;

现在我们假设要求next[x]next[x],并且next[i]next[i](0<i<x)(0的值都已经求出;

我们设 p=k+next[k]−1p = k + next[k] - 1,kk 是使 pp 最大的 ii (0<i<x)(0。

如图所示:



现在整理一下问题:

已知:s[k..p]=s[0..next[k]−1]s[k..p] = s[ 0 .. next[ k ]-1 ],求s[x..n−1]s[x .. n-1]与s[0..n−1]s[0 .. n-1]的最长公共前缀;

解:

由s[k..p]s[k .. p] =s[0..next[k]−1] s[ 0 .. next[ k ]-1 ] 得:

s[x..p]s[x .. p] = s[x−k..next[k]−1]s[x-k .. next[ k ]-1 ] …………………….(1) 这个是显然的


并设 L1=p−x+1L1=p-x+1

因为 x−k<xx-k < x,所以 L2=next[x−k]L2=next[x-k]是已知的,得:

s[0..L2−1]=s[x−k...x−k+L2−1]s[0 .. L2-1] = s[x-k ... x-k+L2-1] …………………….(2)


通过等式(1),(2)可以推出s[0..k1]=s[x..k2]s[0 .. k1] = s[x .. k2]

当L1≤L2L1 ≤ L2时,如下图所示:



表示s[0..L1−1]=s[x..x+L1−1]s[0 .. L1-1] = s[x .. x+L1-1],但不能确定蓝色部分是否相等,所以需要继续比下去。

当L1>L2L1 > L2时,如下图所示:



表示s[0..L2−1]=s[x..x+L2−1]s[0 .. L2-1] = s[x .. x+L2-1],

而且因为L2=next[x−k]L2 = next[x-k],使得s[L2]≠s[x+L2]s[L2] ≠ s[x+L2]

所以next[x]=L2next[x] = L2

证明:

假设s[L2]=s[x+L2]s[L2]=s[x+L2],

又因为s[x+L2]==s[x−k+L2]s[x+L2]==s[x-k+L2]…………由(1)推出

所以s[L2]=s[x−k+L2]s[L2]=s[x-k+L2],

所以next[x−k]=L2+1next[x-k]=L2+1 与 next[x−k]=L2next[x-k]=L2矛盾

求next[]next[]数组的代码如下:

[code]void getNext(char *s, int next[]) {
    int lenS = strlen(s);

    next[0] = lenS;
    int p = 0;
    while (p+1 < lenS && s[p] == s[p+1]) p++;

    next[1] = p;
    int k = 1, L;
    for (int i = 2; i < lenS; i++) {
        p = k + next[k] - 1, L = next[i-k];
        if (i + L <= p)
            next[i] = L;
        else {
            int j = max(p-i+1, 0);
            while (i + j < lenS && s[i+j] == s[j]) j++;
            next[i] = j, k = i;
        } 
    }
}


3. 如何计算extend[]extend[]数组的值

回到原来的问题

此时已经求出next[]next[],我们用extend[]extend[]保存字符串S的所有后缀和字符串T的最长公共前缀的值

求解extend[]extend[]数组的方法 和 求解next[]next[]数组的方法类似,重复上面的过程:

假设要求extend[x]extend[ x ],并且extend[i]extend[ i ] (0<i<x)(0的值都已经求出;

我们设p=k+extend[k]−1p = k + extend[k] - 1, k是使p最大的 ii (0<i<x)(0;

如图所示:



整理一下问题:

已知:s[k..p]=T[0..extend[k]−1]s[k..p] = T[ 0 .. extend[ k ]-1 ],求s[x..n−1]s[x .. n-1]与T[0..m−1]T[0 .. m-1]的最长公共前缀;

解:

由s[k..p]s[k .. p] =T[0..extend[k]−1]T[ 0 .. extend[ k ]-1 ] 得:

s[x..p]s[x .. p] = T[x−k..extend[k]−1]T[x-k .. extend[ k ]-1 ] …………………….(1)


并设 L1=p−x+1L1=p-x+1

因为 x−k<xx-k < x,所以 L2=next[x−k]L2=next[x-k]是已知的,得:

T[0..L2−1]=T[x−k...x−k+L2−1]T[0 .. L2-1] = T[x-k ... x-k+L2-1] …………………….(2)


通过等式(1),(2)可以推出T[0..k1]=s[x..k2]T[0 .. k1] = s[x .. k2]

当L1≤L2L1 ≤ L2时,如下图所示:



表示T[0..L1−1]=s[x..x+L1−1]T[0 .. L1-1] = s[x .. x+L1-1],但不能确定蓝色部分是否相等,所以需要继续比下去。

当L1>L2L1 > L2时,如下图所示:



表示T[0..L2−1]=s[x..x+L2−1]T[0 .. L2-1] = s[x .. x+L2-1],

而且因为L2=extend[x−k]L2 = extend[x-k],使得T[L2]≠s[x+L2]T[L2] ≠ s[x+L2]

所以extend[x]=L2extend[x] = L2

证明:

假设T[L2]=s[x+L2]T[L2]=s[x+L2],

又因为s[x+L2]=T[x−k+L2]s[x+L2]=T[x-k+L2]…………由(1)推出

所以T[L2]==s[x−k+L2]T[L2]==s[x-k+L2],

所以extend[x−k]=L2+1extend[x-k]=L2+1 与 extend[x−k]=L2extend[x-k]=L2矛盾

求extend[]extend[]数组的代码如下:

[code]void getExtend(char *s, char *T, int extend[], int next[]) {
    int lenS = strlen(s) ,lenT = strlen(T);
    getNext(s,next);

    int p = 0;
    while(p < lenS && s[p] == T[p]) p++;

    extend[0] = p;
    int k = 0, L;
    for(int i = 1; i < lenS; i++) {
        p = k + extend[k] - 1, L = next[i - k];
        if(i + L <= p)
            extend[i] = L;
        else {
            int j = max(p-i+1, 0);
            while (i + j < lenS && s[i + j] == T[j]) j++;
            extend[i] = j; k = i;
        } 
    }
}


4. 时间复杂度分析:

对于s串,计算next[]数组的时间是O(n),计算extend[]数组的时间是O(m)的,总算法复杂度是O(n+m);

此博文转自:http://www.cnblogs.com/Rlemon/archive/2013/06/26/3157574.html

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: