所谓拓展kmp,就是拓展的kmp
(逃)
前言
小清新算法,更像一个trick。
exkmp能够在线性复杂度内求出字符串所有后缀与字符串本身的lcp。
个人感觉这个东西和kmp关系不大,反而有些像马拉车的思想。
之前学的东西,现在才想起来写一个解析。
解析
定义
z
i
z_i
zi 表示字符串的后缀
i
i
i 与开头的lcp。
考虑在求完
z
1...
i
−
1
z_{1...i-1}
z1...i−1 的情况下,如何求解
z
i
z_i
zi。
设
p
o
s
pos
pos 为满足
p
o
s
+
z
p
o
s
pos+z_{pos}
pos+zpos 最大的位置,
r
=
p
o
s
+
z
p
o
s
−
1
r=pos+z_{pos}-1
r=pos+zpos−1。
分情况讨论:
- i > r i>r i>r:暴力算出 z i z_i zi,并使 p o s ← i pos\gets i pos←i。
- i ≤ r i\le r i≤r:此时在 r r r 左侧 i i i 的 lcp 情况与 i − p o s + 1 i-pos+1 i−pos+1 是相同的,所以先令 z i ← min ( r − i + 1 , z i − p o s + 1 ) z_i\gets \min(r-i+1,z_{i-pos+1}) zi←min(r−i+1,zi−pos+1),如果这个 min \min min 取的是前一项,就再暴力尝试拓展 z i z_i zi。
和马拉车类似的,当我们暴力计算 z i z_i zi 而产生复杂度时,都必然伴随着 r r r 的增大, r r r 最多不会超过 n n n,所以均摊总复杂度为 O ( n ) O(n) O(n)。
代码
void exkmp(){
z[1]=0;int pl=0;
for(int i=2;i<=n;i++){
if(i>pl+z[pl]-1){
while(s[1+z[i]]==s[i+z[i]]) ++z[i];
}
else{
z[i]=min(pl+z[pl]-i,z[i-pl+1]);
if(z[i]==pl+z[pl]-i){
while(s[1+z[i]]==s[i+z[i]]) ++z[i];
}
}
if(i+z[i]>pl+z[pl]) pl=i;
}
return;
}