Skip to content

2015年 408 数据结构 第 8 题

数据结构2015年选择题2分

题目

已知字符串 s 为 "abaabaabacacaabaabcc",模式串 t 为 "abaabc"。采用 KMP 算法进行匹配,第一次出现"失配"(s[i] ≠ t[j])时,i = j = 5,则下次开始匹配时,i 和 j 的值分别是( )。

错因

A

朴素匹配的回退方式想:失配后把模式串整体右移一位,i 退回到本轮起点的下一位(i=1)、j 重置为 0 重新比。这正是 KMP 要避免的低效回退——朴素法之所以慢就是因为 i 会回退。KMP 的核心承诺是 i 永远不动,只动 j。

B

记得"i 不回退",但误以为 j 总是重置为 0。这相当于把 next[5] 当成 0 用——退化成了"模式串整体后移到 i 处",丢掉了已经匹配上的前缀信息。如果真把 j 设 0,就把 KMP 用成了和 A 等价的朴素法。next[5] 应是 2,不是 0。

D

next[5] = 2 算对了(j 退到 2 没错),但i 也跟着 +1 走到了 6。可能是把"失配后右移一位再继续"的朴素思维混进了 KMP,或把"j 退、i 进"两个动作叠加。KMP 的关键就是失配时 i 静止不动——只有匹配成功的字符才能让 i 前进。

总解析

KMP 的核心规则

情况i 的动作j 的动作
匹配成功(s[i] = t[j])i++j++
失配(s[i] ≠ t[j])不动j = next[j]
失配且 j = 0 已是开头i++j 仍为 0

失配时 i 不回退,是 KMP 比朴素匹配快的根本原因。

计算 t = "abaabc" 的 next 数组(0-indexed,约定 next[j] = "t[0..j-1] 的最长真前缀 = 真后缀"的长度,next[0] 不参与回退):

jt[j]t[0..j-1]最长 真前缀=真后缀 长度next[j]
0a""
1b"a"00
2a"ab"00
3a"aba""a" 长 11
4b"abaa""a" 长 11
5c"abaab""ab" 长 22

"abaab" 的真前缀有 "a", "ab", "aba", "abaa";真后缀有 "b", "ab", "aab", "baab"。共同的最长是 "ab",长度 2。所以 next[5] = 2。

应用 KMP 失配规则

题目给出 i = j = 5 时失配(s[5]='a' ≠ t[5]='c')。

  • i:保持 5 不变
  • j:j ← next[5] = 2

直观图示——模式串向右滑动到 "ab" 与 s 中已匹配的 "ab"(即 s[3..4])对齐,从 t[2] 开始与 s[5] 继续比:

位置:   0 1 2 3 4 5 6 7 ...
s:      a b a a b a a b ...
t (旧):  a b a a b c              ← 旧位置
t (新):        a b a a b c        ← 滑动到此处,从 j=2 开始

              i=5 不动,j=2 接着比

下一次比较的是 s[5]='a' 和 t[2]='a' → 匹配,继续 i++、j++。

为什么 j 退到 2 是对的:已经匹配上的 "abaab"(s[0..4] = t[0..4])后缀里有 "ab" 等于 t 的前缀 "ab"——意味着我们不需要再比较 s[3..4],可以直接把 t 的 "ab" 对齐到这里,从 t[2] 接着往下比。

送命陷阱:把 KMP 的"j ← next[j]"误记为"j ← 0"或者"i ← i+1"。口诀:失配时 i 不动,j 退到 next[j]

最终答案是 C(i = 5, j = 2)

最后更新:

🎬 可视化演示
加载中...

提示:可在可视化区直接操作播放、步进、修改参数