从头至尾彻底理解扩展KMP
1. 扩展KMP问题:
求字符串S的所有后缀和字符串T的最长公共前缀。
扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。
2. 拓展KMP的next[]数组怎么计算?
在解上面这个问题前我们要先解决一个类似的问题:
求字符串s的所有后缀和字符串s本身的最长公共前缀;
我们用next[] 数组保存这些值;
现在我们假设要求
我们设
如图所示:
现在整理一下问题:
已知:
解:
由s[k..p] =s[0..next[k]−1] 得:s[x..p] =s[x−k..next[k]−1] …………………….(1) 这个是显然的并设
L1=p−x+1
因为x−k<x ,所以L2=next[x−k] 是已知的,得:s[0..L2−1]=s[x−k...x−k+L2−1] …………………….(2)通过等式(1),(2)可以推出
s[0..k1]=s[x..k2]
当
表示
当
表示
而且因为
所以
证明:
假设s[L2]=s[x+L2] ,
又因为s[x+L2]==s[x−k+L2] …………由(1)推出
所以s[L2]=s[x−k+L2] ,
所以next[x−k]=L2+1 与next[x−k]=L2 矛盾
求
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[] 数组的值
回到原来的问题
此时已经求出
求解
假设要求
我们设
如图所示:
整理一下问题:
已知:
解:
由s[k..p] =T[0..extend[k]−1] 得:s[x..p] =T[x−k..extend[k]−1] …………………….(1)并设
L1=p−x+1
因为x−k<x ,所以L2=next[x−k] 是已知的,得:T[0..L2−1]=T[x−k...x−k+L2−1] …………………….(2)通过等式(1),(2)可以推出
T[0..k1]=s[x..k2]
当
表示
当
表示
而且因为
所以
证明:
假设T[L2]=s[x+L2] ,
又因为s[x+L2]=T[x−k+L2] …………由(1)推出
所以T[L2]==s[x−k+L2] ,
所以extend[x−k]=L2+1 与extend[x−k]=L2 矛盾
求
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);