字符串匹配-扩展KMP(Extend-KMP)

来源:互联网 发布:网络女作家 编辑:程序博客网 时间:2024/05/16 06:16

首先还是来看看问题:
给出一个长为N的字符串S,再给出一个长为M的字符串T
求S的所有后缀中和T的最长公共前缀
显然可以想到暴力的做法,枚举S所有的后缀,然后和T做匹配,时间复杂度为O(NM)
显然,这个方法和之前的暴力一样,都处理了太多的重复操作,那么可以用类似KMP的方法来处理吗?
答案是肯定的,也就是Extend-KMP算法
可以先用类似KMP的想法,用next数组保存T[i..M1]T[0..M1]的最长公共前缀,但是有一个特例:next[0]=len
接着设p为答案伸得最远的后缀的下标,也就是T[p..M1],但是这个后缀不包括原串,
如果这两个串的公共前缀没有包括T[i],那么就要去暴力求解,如果包括就可以尝试像manacher一样用前面已经求出的答案。
接下来举个栗子:
字符串T=aaaabaaaa
那么next=9,3,2,1,0,4,???
根据之前求出的,可以知道T[0..3]=T[5..8],并且T[1..3]=T[0..2],然后把T[0..3]T[5..8]各去掉相同位置的一位,仍然相等,也就是T[1..3]=T[6..8]
根据之前的结果推导出T[6..8]=T[1..3]=T[0..2]因此,next[6]应该至少为3
然后检查是否能够增加,发现S[9]S[3] 所以next[6]不能增加,也就是3
用类似这样的方法,同样可以直接推导出extend的结果,那么就得到了答案。
以下为代码:

void get_next() {    int len = strlen(T);    _next[0] = len;    int a = 0;    while(a < len - 1 && T[a] == T[a + 1]) {        ++a;    }    _next[1] = a;    a = 1;    for(int k = 2; k < len; ++k) {        int p = a + _next[a] - 1;        int L = _next[k - a];        if(k - 1 + L >= p) {            int j = (p - k + 1) > 0 ? (p - k + 1) : 0;            while(k + j < len && T[k + j] == T[j]) {                ++j;            }            _next[k] = j;            a = k;        } else {            _next[k] = L;        }    }}void ExtendKMP() {    int i = 0, j, po, lenS = strlen(S), lenT = strlen(T);    get_next();    while(S[i] == T[i] && i < lenT && i < lenS) {        i++;    }    ex[0] = i;    po = 0;    for(i = 1; i < lenS; i++) {        if(_next[i - po] + i < ex[po] + po) {            ex[i] = _next[i - po];        } else {            j = ex[po] + po - i;            if(j < 0) {                j = 0;            }            while(i + j < lenS && j < lenT && S[j + i] == T[j]) {                j++;            }            ex[i] = j;            po = i;        }    }}

代码有点长……
最后来看看时间复杂度:
先看求next
因为内部while每运行一次,j至少加1,最多执行M次,再加上外层循环的语句,时间复杂度为O(M)
extend同理,所以总时间为O(M+N)
但是相对于KMP来说,这个算法的常数比较差,所以实际运行时应该会慢一点,但对于不是线性时间求出答案的方法,是很快的。

0 0
原创粉丝点击