关于KMP算法,比较难以理解的一部分应该是构造next数组:
void GetNext(char* p,int next[])
{
int pLen = strlen(p);
next[0] = 0, next[1] = 0;
for(int j = 1; j < pLen; ++j)
{
int k =next[j];
while(k && p[k] != p[j])
k = next[k];
next[j + 1] = p[j] == p[k] ? k + 1: 0;
}
}
next数组说白了代表第i个下标前的字符串,最大的公共前后缀的长度
如s=ABCDAB,对整个字符串而言最大公共前后缀为AB,因此next[6] = 2 (i = 6, i之前的字符串即s[0] ~ s[5])
根据上述,可以先构造一部分的next数组,则对当前
P
j
P_j
Pj, 表示DADCDA有最长公共前后缀DA,长度为2,即
P
k
−
2
P
k
−
1
=
P
j
−
2
P
j
−
1
P_{k-2}P_{k-1}=P_{j-2}P_{j-1}
Pk−2Pk−1=Pj−2Pj−1,而
P
k
−
2
P
k
−
1
=
P
0
P
1
P_{k-2}P_{k-1}=P_0P_1
Pk−2Pk−1=P0P1,因此
P
0
P
1
=
P
j
−
2
P
j
−
1
P_{0}P_{1}=P_{j-2}P_{j-1}
P0P1=Pj−2Pj−1
则对于j + 1的字符,由于
P
j
=
P
k
P_j=P_k
Pj=Pk,因此next[7]=2+1=3,也可以看出来A之前的字符串DADCDAD,有最长公共前后缀DAD,长度为3
更新next数组之后,对于当前
P
j
=
3
P_j=3
Pj=3, 有
P
k
−
3
P
k
−
2
P
k
−
1
=
P
0
P
1
P
2
=
P
j
−
3
P
j
−
2
P
j
−
1
P_{k-3}P_{k-2}P_{k-1}=P_{0}P_{1}P_{2}=P_{j-3}P_{j-2}P_{j-1}
Pk−3Pk−2Pk−1=P0P1P2=Pj−3Pj−2Pj−1
由于
P
j
!
=
P
k
P_j != P_k
Pj!=Pk, 递归更新k = next[k]=1,即代表
P
k
−
3
=
P
k
−
1
P_{k-3}=P_{k-1}
Pk−3=Pk−1,即
P
0
=
P
2
P_0=P_2
P0=P2
根据对称性可以知道
P
j
−
3
=
P
j
−
1
=
P
0
=
P
2
P_{j-3}=P_{j-1}=P_0=P_2
Pj−3=Pj−1=P0=P2
此时
P
k
=
P
1
=
P
j
=
A
P_k=P_1=P_j=A
Pk=P1=Pj=A,即
P
0
P
1
=
P
j
−
1
P
j
P_0P_1=P_{j-1}P_j
P0P1=Pj−1Pj,即最大公共前后缀为2,因此赋值next[8]=k + 1 = 1 + 1 = 2
若有更复杂的字符串,循环该过程即可,直到找到
P
j
=
P
k
P_j = P_k
Pj=Pk或循环到首字符