扩展KMP(exkmp)

扩展KMP

e x k m p exkmp exkmp 求解的问题:对于给定的主串 S S S ,和模式串 T T T ,求出主串 S S S 的所有后缀与模式串 T T T 的最长公共前缀长度

K M P KMP KMP 求解的问题是在主串 S S S 中模式串T出现的次数和位置, 扩 展 K M P 扩展KMP KMP 求解问题包含了 K M P KMP KMP 求解的问题,因为主串 S S S 中与模式串 T T T 的最长公共前缀长度等于 ∣ T ∣ |T| T 的后缀就是 K M P KMP KMP 所求。


e x k m p exkmp exkmp 算法求解过程实际是求两个数组的值:

n e t [ i ] net[i] net[i] : 模式串 T T T 的后缀 T s u f ( i ) Tsuf(i) Tsuf(i) 与模式串 T T T 本身的最长公共前缀长度 (net指next,C++11中next为保留字)

e x t e n d [ i ] extend[i] extend[i]:主串 S S S 的后缀 S s u f ( i ) Ssuf(i) Ssuf(i) 与模式串 T T T 的最长公共前缀长度


exkmp算法采用 递推求解 e x t e n d [ i ] extend[i] extend[i] 的值可以由 e x t e n d [ 0.. i ] extend[0..i] extend[0..i] n e t net net 数组的值求得

算法推导:

设:主串 S S S ,长度 N N N ,模式串 T T T ,长度 M M M ,下标均从 0 0 0 开始。
假设 e x t e n d [ 0.. i ] extend[0..i] extend[0..i] n e t net net 数组已知,现在正在求解 e x t e n d [ k ] extend[k] extend[k] 的值

设最远匹配位置为 p p p (即主串 S S S 下标分别从 0 0 0 ~ K + 1 K+1 K+1 开始与模式串T每次下标从 0 0 0 开始进行匹配可以到达的主串 S S S 的最大下标),则 p = m a x ( i + e x t e n d [ i ] − 1 ) p = max( i + extend[i]- 1 ) p=max(i+extend[i]1) i ∈ [ 0 , k − 1 ] i\in[0,k-1] i[0,k1] (匹配起始位置 + + + 匹配长度 − - 1 1 1 = = = 匹配末端位置)

j j j m a x max max 取得最大值的下标,即

p = j + e x t e n d [ j ] − 1 p = j + extend[j] - 1 p=j+extend[j]1

根据 e x t e n d [ j ] extend[j] extend[j] 的含义得到:

S [ j . . p ] = T [ 0.. p − j ] S[j..p] = T[0..p-j] S[j..p]=T[0..pj]

( p ≥ k ) (p\geq k) (pk) 时 上式两边同时删去 [ j . . k − 1 ] [j..k-1] [j..k1] 得到:

S [ k . . p ] = T [ k − j . . p − j ] S[k..p] = T[k - j..p - j] S[k..p]=T[kj..pj] . . . . . . . . (1)

l = n e t [ k − j ] l = net[k-j] l=net[kj] ,根据 n e t net net 数组的定义有:

T [ 0.. l − 1 ] = T [ k − j . . k − j + l − 1 ] T[0..l-1] = T[k - j..k - j + l - 1] T[0..l1]=T[kj..kj+l1] . . . . . . . . (2)

n e t [ k − j ] net[k-j] net[kj] 的原因是 (1) 式中 T T T 的下标是从 k − j k-j kj 开始的。

- C a s e 1 : Case 1: Case1:
p − j > k − j + l − 1 p - j > k - j + l - 1 pj>kj+l1( (1) 式 T T T 右端下标 > > > (2) 式 T T T 右端下标 ),
p > k + l − 1 p > k + l -1 p>k+l1 时 结合 (1)、(2) 式有:

S [ k . . k + l − 1 ] = T [ k − j . . k − j + l − 1 ] = T [ 0.. l − 1 ] S[k..k + l - 1] = T[k - j..k - j + l -1] = T[0..l - 1] S[k..k+l1]=T[kj..kj+l1]=T[0..l1]

所以此时 e x t e n d [ k ] = l extend[k] = l extend[k]=l

- C a s e 2 : Case 2: Case2:
p − j ≤ k − j + l − 1 p - j \leq k - j + l - 1 pjkj+l1( (1) 式 T T T 右端下标 ≤ \leq (2) 式 T T T 右端下标 ),
p ≤ k + l − 1 p \leq k + l -1 pk+l1 时 结合 (1)、(2)式有:

S [ k . . p ] = T [ 0.. p − k ] S[k..p] = T[0..p - k] S[k..p]=T[0..pk]

但从 S [ p + 1 ] S[p+1] S[p+1] 开始就不确定了
此时从主串 S S S 从下标 p + 1 p + 1 p+1 开始,模式串 T T T 从下标 p − k + 1 p - k + 1 pk+1 开始进行匹配,
e x t e n d [ k ] = l + 额 外 匹 配 长 度 extend[k] = l + 额外匹配长度 extend[k]=l+
最后更新 j j j p p p 的值, j j j 的值直接等于 k k k ,因为最远匹配位置明显超过 p p p 了。

上面算法推导中有个小前提: p ≥ k p \geq k pk
p < k p < k p<k 时,必定有 p ≤ k + l − 1 p \leq k + l -1 pk+l1,所以可以放到 C a s e 2 Case 2 Case2 里面,只不过此时的匹配起始位置为:主串 S S S 从下标 k k k 开始,模式串 T T T 下标从 0 0 0 开始。


n e t net net 数组求解: n e t net net 数组可以看做是 以 T T T 为主串, T T T 为模式串的一个特殊的 e x k m p exkmp exkmp,所以可以用上面的方法求解。将上面算法过程中的 e x t e n d [ i ] extend[i] extend[i] n e t [ i ] net[i] net[i] 替换就行了。

因为 需要用到 n e t [ k − j ] net[k - j] net[kj] k − j ≤ k k-j \leq k kjk,当且仅当 j = 0 , k = 1 j=0,k=1 j=0k=1 时取等号,所以先把net[1]暴力求解就好。


求解 n e t net net 数组的代码:

void getNet(string& t){
    int len = t.length(),j=0,p,l;
    net[0] = len;
    while(j<len&&t[1+j]==t[j]) j++; //求 net[1]
    net[1] = j;
    j = 1;
    for(int k=2;k<len;k++){
        p=j+net[j]-1;l=net[k-j];
        if(p<=k+l-1){ // Case 2
            int i=p>=k?p-k+1:0; //p是否大于等于k,i为从k开始的匹配长度
            while(k+i<len&&t[k+i]==t[i]) i++;
            net[k]=i;
            j=k; //更新j
        }
        else net[k]=l; // Case 1
    }
}

e x t e n d extend extend 数组的代码:

void getExtend(string& s,string& t){
    int ls = s.length(),lt =t .length(),j=0,p,l;
    while(j<ls&&j<lt&&s[j]==t[j]) j++; //求 extend[0]
    extend[0] = j;
    j = 0;
    for(int k=1;k<ls;k++){
        p=j+extend[j]-1;l=net[k-j];
        if(p<=k+l-1){ // Case 2
            int i=p>=k?p-k+1:0; //p是否大于等于k,i为从k开始的匹配长度
            while(k+i<ls&&i<lt&&s[k+i]==t[i]) i++;
            extend[k]=i;
            j=k; //更新j
        }
        else extend[k]=l; // Case 1
    }
}

你可能感兴趣的:(字符串)