参考和图片来自 这篇博客
exkmp是求解 最长共同前缀长度 的算法
重新定义 next 数组:
next[i] : 满足 t[i…m-1]=t[0…m-1] 的最长公共前缀
即:以下标 0 为首和以下标 i 为首的串的最长公共前缀长度
引入一个新的数组:
extend[i] : s[i…n-1] 与 t[0…m-1] 的最长公共前缀
即:以下标 i 为首的s串 和 以下标 0 为首t串 的最长相同前缀
在完成extend[0…k]的匹配后,定义匹配过程中所达到的最远位置为P:
p = i+extend[i]-1 最大时 的位置(0<=i<=k)
并且设取这个最大值的起始位置为po
即可表示为:S [ po , P ] = T [ 0 , P - po ]
计算 extend [ k + 1 ] :
由上式可以推出:S [ k + 1 , P ] = T [ k +1 - po , P - po ]
令 l = next [ k + 1 - po ]
考虑 2 种情况:
- ① k + l < p
所以:S [ k + 1 , k + l ] = T [ 0 , l - 1 ]
如果 S [ k + l + 1] == T [ l ]
则有 S [ k + 1 , k + l + 1 ] = T [ 0 , l ] = T [ k + 1 - po , k + l + 1 - po ]
(由上面加粗的式子得出)
那么 next [ k + 1 - po ] = l + 1 (≠ l) 与 l 的定义矛盾,所以不存在
因此:extend [ k + 1] = l
注意:代码里是从 i - 1 的状态推到 i 的状态,所以 l = nx[ i - po ]
- ② k + l >= p
S [ P + 1 ] 之后都没有匹配过
所以从 S [ P + 1 ] 和 T [ P + 1 - k ]匹配,直到发生失配为止
此时 extend [ i ] 等于新匹配的长度+ (p+1-k),更新po的位置
计算next数组的过程和计算extend[i]的过程完全一样 将它看成是以T为母串,T为字串的特殊的拓展kmp算法匹配就可以了
int nx[maxn];
int ex[maxn];
void getnx(string x){
int m=x.size();
nx[0]=m;
int j=0,po=1;
while (j+1<m && x[j]==x[j+1]) j++;
nx[1]=j;
for (int i=2; i<m; i++){
int p=nx[po]+po-1;
int l=nx[i-po];
if (i+l<p+1) nx[i]=l;
else {
j=max(0,p-i+1);
while (i+j<m && x[i+j]==x[j]) j++;
nx[i]=j;
po=i;
}
}
}
void exkmp(string s, string t){
int n=s.size(),m=t.size();
getnx(t);
int j=0,po=0;
while (j<n && j<m && s[j]==t[j]) j++;
ex[0]=j;
for (int i=1; i<n; i++){
int p=ex[po]+po-1;
int l=nx[i-po];
if (i+l<p+1) ex[i]=l;
else {
j=max(0,p-i+1);
while (i+j<n && j<m && s[i+j]==t[j]) j++;
ex[i]=j;
po=i;
}
}
}