码迷,mamicode.com
首页 > 其他好文 > 详细

扩展KMP模板

时间:2016-06-14 23:52:17      阅读:219      评论:0      收藏:0      [点我收藏+]

标签:

扩展KMP:
    给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0 <= i < lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i + z - 1]==B[0 .. z - 1]的最大的z值)。
    扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。
【算法】
    设next[i]为满足B[i..i + z - 1] == B[0..z - 1]的最大的z值(也就是B的自身匹配)。
    设目前next[0..lenB - 1]与ex[0..i - 1]均已求出,要用它们来求ex[i]的值。
    设p为目前A串中匹配到的最远位置,k为让其匹配到最远位置的值(或者说,k是在0 <= i0 < i的所有i0值中,使i0 + ex[i0] - 1的值最大的一个,p为这个最大值,即k + ex[k] - 1),显然,p之后的所有位都是未知的,也就是目前还无法知道A[p + 1..lenA - 1]中的任何一位和B的任何一位是否相等。
    根据ex的定义可得,A[k..p] == B[0..p - k],因为i > k,所以又有A[i..p] == B[i - k..p - k],设L = next[i - k],则根据next的定义有B[0..L - 1] == B[i - k..i - k + L - 1]。
    考虑i - k + L - 1与p - k的关系:

    (1)i - k + L - 1 < p - k,即i + L <= p。
        这时,由A[i..p] == B[i - k..p - k]可以得到A[i..i + L - 1] == B[i - k..i - k + L - 1],又因为B[0..L - 1] == B[i - k..i - k + L - 1]所以A[i..i + L - 1] == B[0..L - 1],这就说明ex[i] >= L。
        又由于next的定义可得,A[i + L]必然不等于B[L](否则A[i..i + L]==B[0..L],因为i+L<=p,所以A[i..i + L] == B[i - k..i - k + L],这样B[0..L] == B[i - k..i - k + L],故next[i - k]的值应为L + 1或更大),这样,可以直接得到ex[i] = L!

    (2)i + k - L + 1 >= p - k,即i + L > p。
        这时,首先可以知道A[i..p]和B[0..p - i]是相等的(因为A[i..p] == B[i - k..p - k],而i + k - L + 1 >= p - k,由B[0..L - 1] == B[i - k..i - k + L - 1]可得B[0..p - i] == B[i - k..p - k],即A[i..p] == B[0..p - i]),然后,对于A[p + 1]和B[p - i + 1]是否相等,目前是不知道的(因为前面已经说过,p是目前A串中匹配到的最远位置,在p之后无法知道任何一位的匹配信息),因此,要从A[p + 1]与B[p - i + 1]开始往后继续匹配(设j为目前B的匹配位置的下标,一开始j = p - i + 1,每次比较A[i + j]与B[j]是否相等,直到不相等或者越界为止,此时的j值就是ex[i]的值)。在这种情况下,p的值必然会得到延伸,因此更新k和p的值。
   
    边界:ex[0]的值需要预先求出,然后将初始的k设为0,p设为ex[0]-1。
    对于求next数组,也是“自身匹配”,类似KMP的方法处理即可。唯一的不同点也在边界上:可以直接知道next[0] = lenB,next[1]的值预先求出,然后初始k = 1,p = ex[1]。
   
    严重注意:在上述的情况(2)中,本该从A[p + 1]与B[p - i + 1]开始匹配,但是,若p + 1 < i,也就是p - i + 1 < 0(这种情况是有可能发生的,当ex[i - 1] = 0,且前面的ex值都没有延伸到i及以后的时候)的话,需要将A、B的下标都加1(因为此时p必然等于i - 2,如果A、B的下标用两个变量x、y控制的话,x和y都要加1)!!


模板代码:

#include <iostream>
#include <cstring>

using namespace std;

const int n = 500004;
int next[n];
int extend[n];
char S[n];
char T[n];

void GetNext(char* T)
{
    int k = 0;
    int Tlen = strlen(T);
    next[0] = Tlen;
    while(k < Tlen - 1 && T[k] == T[k + 1])
        k++;
    next[1] = k;
    k = 1;
    for(int i = 2; i < Tlen; i++)
    {
        int p = k + next[k] - 1, L = next[i - k];
        if(i + L - 1 >= p)
        {
            int j = (p - i + 1) > 0 ? p - i + 1 : 0;
            while(i + j < Tlen && T[i + j] == T[j])
                j++;
            next[i] = j;
            k = i;
        }
        else next[i] = L;
    }
}

void GetExtend(char* S, char* T)
{
    int k = 0;
    GetNext(T);
    int Slen = strlen(S);
    int Tlen = strlen(T);
    int MinLen = Slen < Tlen ? Slen : Tlen;
    while(k < MinLen && S[k] == T[k])
        k++;
    extend[0] = k;
    k = 0;
    for(int i = 1; i < Slen; i++)
    {
        int p = k + extend[k] - 1, L = next[i - k];
        if(i + L - 1 >= p)
        {
            int j = (p - i + 1) > 0 ? p - i + 1 : 0;
            while(i + j < Slen && j < Tlen && S[i + j] == T[j])
                j++;
            extend[i] = j;
            k = i;
        }
        else extend[i] = L;
    }
}

扩展KMP模板

标签:

原文地址:http://www.cnblogs.com/GlassHour/p/5585704.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!