Advertisement

扩展KMP(exkmp)

阅读量:

扩展KMP

该算法旨在解决的核心问题是:针对输入的主串 S 和给定的模式串 T ,计算并确定其所有后缀与模式串 T 的最长公共前缀的长度。

该算法旨在通过主串S与模式串T之间的匹配来确定其出现频率及具体位置。而扩展型KMP算法则在此基础上增加了更多功能与应用场景。


exkmp 算法求解过程实际是求两个数组的值:

next[i] : 字符串 T 的后缀 Tsuf(i) 与其自身的最长公共前缀 (在 C++11 中 next 是保留字)

extend[i] :主串 S 的后缀 Ssuf(i) 与模式串 T 的最长公共前缀长度


exkmp算法基于 递推计算 ,其中extend[i]的值能够通过extend[0..i]net数组的数值进行计算得到。

算法推导:

令基准字符串 S 长度为 N, 目标模式 T 长度为 M, 索引从零开始。
当前正致力于计算 extend[k] 的值, 已知 extend[0..i] 和 net 数组已预知。

令最远匹配的位置为p(其中主串S的下标从0至K+1开始与模式串T每次以初始下标0进行匹配所能达到的最大主串S的位置),则满足条件的最大值p = \max(i + \text{extend}[i] - 1) ,其中i\in[0, k-1](这里\text{extend}[i]表示第i个字符之后能够扩展的最大长度)。(即:\text{extend}[i]对应的是当前字符之后能够连续匹配的有效长度)

jmax 取得最大值的下标,即

p = j + extend[j] - 1

根据 extend[j] 的含义得到:

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

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

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

l = net[k-j] ,根据 net 数组的定义有:

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

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

-Case 1:
p - j > k - j + l - 1( (1) 式 T 右端下标 > (2) 式 T 右端下标 ),
p > k + l -1 时 结合 (1)、(2) 式有:

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

所以此时 extend[k] = l

-Case 2:
p - j \leq k - j + l - 1( (1) 式 T 右端下标 \leq (2) 式 T 右端下标 ),
p \leq k + l -1 时 结合 (1)、(2)式有:

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

而在此时,在主串的位置p+1处开始就不再明确。 当主串中的位置在p+1时,在模式串中对应的位置是p−k+1处开始进行匹配。 其中 extend[k] 的计算结果等于基础长度l与额外增加的部分之和。
随后更新j和p的值。
因为最远匹配位置明显超过了当前的$j位置。

在上述算法推导过程中有一个重要前提是主串S与模式串T之间必须满足某种对应关系。当不满足上述小前提时(即当p < k时不等式不成立),必然满足另一个条件即p ≤ k + l -1。因此可将其归入Case 2处理。此时匹配的起始位置为:主串S从索引k的位置开始与模式串T进行比较


求解 net 数组:即该数组相当于其中以 T 为主串的特殊形式(特殊 exkmp),因此可采用前述方法进行计算。在上述算法中将 extend[i] 替代为 net[i] 即可。

考虑到 涉及到 net[k - j]k-j \leq k 的情况出现时(当且仅当 j=0k=1 时)会出现等式成立的情形),因此建议直接计算 net[1] 的值即可完成问题解决。


求解 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
    }
    }
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解释

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
    }
    }
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解释

全部评论 (0)

还没有任何评论哟~