4.1 前后缀概念
串的前缀:一定包含第一个字符,但是不包含最后一个字符的子串
串的后缀:一定包含最后一个字符,但是不包含第一个字符的子串
4.2 求next数组(手算原理)
原理:
当第j个字符
匹配失败的时候,有前1~j-1
个字符组成的串记为S,则next[j]=S的前后缀相等的最长长度+1
。
特别的有
next[1]=0
next[2]=1
求ababaa的next数组:
序号 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
模式串 | a | b | a | b | a | a |
next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
4.3 求next数组伪代码
模式串为
p
1
p
2
.
.
.
p
k
−
1
p
k
.
.
.
.
p
j
−
k
+
1
.
.
.
p
j
−
1
p
j
.
.
.
p
m
p_1p_2...p_{k-1}p_k....p_{j-k+1}...p_{j-1}p_j...p_m
p1p2...pk−1pk....pj−k+1...pj−1pj...pm
算法步骤:
- 首先
next[1]=0
- 设
next[j]=k
其中k满足
假设此时与模式中的第k个字符进行比较,则模式中前k-1个字符的子串必须满足下列条件,且不存在一个比k更大的值满足下列条件:
p 1 p 2 . . . p k − 1 = p j − k + 1 p j − k + 2 . . . p j − 1 p_1p_2...p_{k-1}=p_{j-k+1}p_{j-k+2}...p_{j-1} p1p2...pk−1=pj−k+1pj−k+2...pj−1 - 此时
next[j+1]
可能有两种情况:- 若
p
k
=
p
j
p_k = p_j
pk=pj
表明在模式串中 p 1 p 2 . . . p k = p j − k + 1 p j − k + 2 . . . p j p_1p_2...p_{k}=p_{j-k+1}p_{j-k+2}...p_{j} p1p2...pk=pj−k+1pj−k+2...pj,并且不存在一个更大的k值满足上述条件,此时next[j+1]=k+1
,可以化为next[j+1]=next[j]+1
- 若
p
k
≠
p
j
p_k \neq p_j
pk=pj
表明在模式串中 p 1 p 2 . . . p k ≠ p j − k + 1 p j − k + 2 . . . p j p_1p_2...p_{k} \neq p_{j-k+1}p_{j-k+2}...p_{j} p1p2...pk=pj−k+1pj−k+2...pj。
问题变成:用前缀 p 1 p 2 . . . p k p_1p_2...p_{k} p1p2...pk去跟后缀 p j − k + 1 p j − k + 2 . . . p j p_{j-k+1}p_{j-k+2}...p_{j} pj−k+1pj−k+2...pj进行匹配,也就是求前缀的前缀和后缀的后缀的最长相等字符个数(相当于原问题的子问题,模式串的规模减小)。
那么当 p k ≠ p j p_k \neq p_j pk=pj的时候,需要将 p 1 p 2 . . . p k p_1p_2...p_{k} p1p2...pk移动成 p 1 p 2 . . . p n e x t [ k ] p_1p_2...p_{next[k]} p1p2...pnext[k],此时比较 p n e x t [ k ] p_{next[k]} pnext[k]与 p j p_j pj,如果还是不匹配,需要寻找长度更短的相等前后缀,下一步继续用 p n e x t [ n e x t [ k ] ] p_{next[next[k]]} pnext[next[k]]与 p j p_j pj进行比较,直到找到某一个更小的 k ′ = n e x t [ n e x t . . . [ k ] ] k'=next[next...[k]] k′=next[next...[k]]使得其满足条件 p 1 p 2 . . . p k ′ = p j − k ′ + 1 p j − k ′ + 2 . . . p j p_1p_2...p_{k'}=p_{j-k'+1}p_{j-k'+2}...p_{j} p1p2...pk′=pj−k′+1pj−k′+2...pj,则 n e x t [ j + 1 ] = k ′ next[j+1]=k' next[j+1]=k′,如果不存在,就令 n e x t [ j + 1 ] = 1 next[j+1]=1 next[j+1]=1
- 若
p
k
=
p
j
p_k = p_j
pk=pj
伪代码:
void get_next(String T, int next[]){
int i = 1, j = 0; // 第一位不存字符
next[1]=0; // next[1]首位为0
while(i < T.length){
if(j==0 || T.ch[i] == T.ch[j]){
++i; ++j;
next[i] = j; //next从2开始,next[i] = next[j]+1
}else{
j = next[j]; //如果不满足,循环找到最小的j
}
}
}