: `4 a# u) ?# g- Y3 H提取加速匹配的信息4 |3 S3 h7 n5 L* w% z9 K: a
上面说道 KMP 算法主要是通过消除主串指针的回溯来提高匹配的效率的,那么,它是则呢样来消除回溯的呢?就是因为它提取并运用了加速匹配的信息!2 W# F, x4 r1 @: c& D
这种信息就是对于每模式串 t 的每个元素 t j,都存在一个实数 k ,使得模式串 t 开头的 k 个字符(t 0 t 1…t k-1)依次与 t j 前面的 k(t j-k t j-k+1…t j-1,这里第一个字符 t j-k 最多从 t 1 开始,所以 k < j)个字符相同。如果这样的 k 有多个,则取最大的一个。模式串 t 中每个位置 j 的字符都有这种信息,采用 next 数组表示,即 next[ j ]=MAX{ k }。/ v( ~8 S; m/ T/ |
$ W! |: s. q3 g; O 加速信息,即数组 next 的提取是整个 KMP 算法中最核心的部分,弄懂了 next 的求解方法,也就弄懂了 KMP 算法的十之七八了,但是不巧的是这部分代码恰恰是最不容易弄懂的…… 6 l& Q* L" E& L: p. V4 G 8 J2 u7 s. D h1 T; l, h1 H. q
先上代码 ) O$ ? `0 |# g$ b8 \3 k7 V . _- R* J2 v M& F p. x, M. q( p' L( y& W0 \
void Getnext(int next[],String t) # i" V* I/ H, T0 T{ $ p' Z' t z* w& E/ s- W& ` int j=0,k=-1; & E1 t8 b5 o P$ G: p next[0]=-1;/ a3 ]+ }6 o5 g4 Z
while(j<t.length-1)# [: B1 e$ l& [. q+ f" a9 y: X5 U
{ 0 [$ y6 b0 n5 A! a7 t8 l if(k == -1 || t[j] == t[k]) C, L5 d' `* M. P3 n& J
{ ; {) d1 ~2 y1 G* }* O% P1 f& x j++;k++;7 j* v+ _$ { N
next[j] = k;# y+ N: q9 b4 v9 r. A* \
}1 s: [4 S8 q9 a; u
else k = next[k];//此语句是这段代码最反人类的地方,如果你一下子就能看懂,那么请允许我称呼你一声大神!+ \- g# b: y0 b) f6 J% C
}) N" O$ t6 I1 x- C2 N3 k) L
}; _; a4 X: e! @$ D; q& ~
3 G0 `9 I, e3 @ok,下面咱们分三种情况来讲 next 的求解过程 2 P7 B- D9 B! {- ^+ O) H' K 8 T; t2 e, [; w# r7 z/ r F 2 E6 Q9 V4 y) C特殊情况 6 s' g4 F- K/ {当 j 的值为 0 或 1 的时候,它们的 k 值都为 0,即 next[0] = 0、next[1] =0。但是为了后面 k 值计算的方便,我们将 next[0] 的值设置成 -1。 3 A$ H# D1 E+ w# g* n( M" ~% N5 ~: F8 M9 d0 p
' v+ B2 `9 [- ?0 ]9 X3 a/ N v
当 t[j] == t[k] 的情况0 ~# U0 o! a0 [* b% b
举个栗子 ( \$ g+ V! \8 k6 M' L/ v ]/ e
1 r E4 G4 p) n5 n* @9 l4 x 由第2中情况可知,当 t[j] == t[k] 时,t[j+1] 的最大子串的长度为 k,即 next[j+1] = k+1。但是此时t[j] != t[k] 了,所以就有 next[j+1] < k,那么求 next[j+1] 就等同于求 t[j] 往前小于 k 个的字符(包括t[j],看上图蓝色框框)与 t[k] 前面的字符(绿色框框)的最长重合串,即 t[j-k+1] ~ t[j] 与 t[0] ~ t[k-1] 的最长重合串(这里所说“最长重合串”实不严谨,但你知道是符合 k 的子串就行…),那么就相当于求 next[k](只不过 t[k] 变成了 t[j],但是 next[k] 的值与 t[k] 无关)!!!。所以才有了这句 k = next[k],如果新的一轮循环(这时 k = next[k] ,j 不变)中 t[j] 依然不等于 t[k] ,则说明倒数第二大 t[0~next[k]-1] 也不行,那么 k 会继续被 next[k] 赋值(这就是所谓的 k 回退…),直到找到符合重合的子串或者 k == -1。2 T6 p' g& d4 ]: U
* `( ? a5 ]5 o% A) h$ i* h" C8 r6 Q# C$ R7 o$ L5 F/ Z, U
至此,算是把求解数组 next 的算法弄清楚了(其实是,终于把 k = next[k] 弄懂了…) " C9 k9 r m, I! B' z3 T0 U$ N- B; Q' t2 W
1 x f% N/ _& u8 o. r7 h因为这个算法神奇难解之处就在k=next[k]这一处的理解上,网上解析的非常之多,有的就是例证,举例子按代码走流程,走出结果了,跟肉眼看的一致,就认为解释了为什么k=next[k];很少有看到解释的非常清楚的,或者有,但我没有仔细和耐心看下去。我一般扫一眼,就大概知道这个解析是否能说的通。仔细想了三天,搞的千转百折,山重水复,一头雾气缭绕的。搞懂以后又觉得确实简单,但是绕人,烧脑。6 _+ T0 ?9 J( o, S5 p l; Y8 g% y. i
& A, Q1 w) g1 _
5 [! |4 o8 v2 O, W* W: f4 L* E) R再此特别感谢昵称为“sofu6”的博客园主,正是他的博客,让我这愚笨的脑袋瓜开窍了 & ~8 i' _* c' i* y Z' \4 J; i" k- F' G