deepseek详细讲解KMP算法中的next数组
##角色
你是一个精通计算机算法的大学老师
#背景
一名大学生正在学习KMP算法
#任务
解答该学生的疑惑
##疑惑
该学生不明白j = b[j - 1];中为什么是”j - 1“
//构建next数组的过程
//len2是模式字符串的长度
//s2是模式串
int j = 0;
for(int i = 1 ; i < len2 ; i++){
while(j > 0 && s2[j] != s2[i]) {
j = b[j - 1];
}
if(s2[j] == s2[i]) j++;
b[i] = j;
}
为什么是"j = b[j - 1]"
1. 基本概念回顾
-
b数组(Next数组)的定义:b[i]的值是:在模式串s2中,以i这个位置结尾的子串(即s2[0...i])中,最长的相等前后缀的长度。 -
i和j的含义:-
i:当前正在计算的b[i]的索引。它指向后缀的最后一个字符。 -
j:它有两个作用,而且这两个作用紧密相关:-
指向前缀的最后一个字符。
j指向的位置,意味着我们已经匹配了长度为j的前缀。例如,当j=2时,表示前缀s2[0]和s2[1]已经匹配成功,现在正在检查s2[2]是否能和s2[i]匹配。 -
代表 当前 最长相等前后缀的长度(当前:指的是匹配到s[i - 1]时,能够匹配s[0]...s[i-1]的众多前后缀个数中最长的那一个)
-
-
结论:在比较 s2[i] 和 s2[j] 之前,j 的值就是已经匹配好的前缀长度。
2. 为什么发生失配(while 循环)
代码的执行逻辑是这样的:
我们想要求 b[i],也就是找 s2[0...i] 这个串的最长相等前后缀。我们利用已有的 b[0] 到 b[i-1] 的信息。
-
初始状态:我们希望在
s2[0...i-1]这个串的某个前缀后面,加上s2[i],如果能和s2[i]匹配上,就大功告成。j此刻正指向这个可能的前缀的末尾。 -
检查匹配:我们检查
s2[j](前缀的下一个字符)和s2[i](当前字符)是否相等。 -
失配发生:如果
s2[j] != s2[i],就说明长度为j的前缀后面接上s2[i]这条路走不通。我们不能再直接用这个长度为j的前缀了。
3. 核心:失配后怎么办?—— j = b[j-1]
找一个更短的前后缀,再尝试能否接上它。
-
b[j-1]里存的是什么?
"j - 1" 时长度为 j 的最长前后缀(即s2[0...j-1])b[j-1]就是s2[0...j-1]中的最长前后缀
相当于:长度为J的不能用,再找一个第二长的前后缀 -
所以,
j = b[j-1]的逻辑是:-
当前长度为
j的前缀匹配失败。 -
我们退而求其次,在已经匹配好的这个前缀
s2[0...j-1]里,找到它的最长相等前缀(其长度就是b[j-1])。 -
把这个更短的前缀的长度作为新的
j。 -
j现在指向了这个新前缀的末尾,我们再次进入while循环,检查这个新前缀的下一个字符s2[j]是否和s2[i]相等。
-
4. 举例说明
假设模式串 s2 是 "ABACABAB",我们在计算到 i=7(最后一个 'B')时卡住了。
-
此时
j是多少?在进入i=7的循环前,j=3(因为b[6]的值是3,表示"ABACABA"有长度为3的相等前后缀"ABA")。j指向s2[3],也就是'C'。 -
第1次匹配:
s2[j]是'C',s2[i]是'B'。'C' != 'B',失配!我们需要缩小j。 -
为什么是
j-1?
我们需要在s2[0...j-1]也就是s2[0...2](即"ABA")里找它的最长相等前后缀。-
b[j-1] = b[2]。b[2]是多少?对于"ABA",最长相等前后缀是"A",长度为1。所以b[2] = 1。 -
因此,新的
j变为 1。j现在指向s2[1],也就是'B'。
-
-
第2次匹配:
s2[j]现在是'B',s2[i]是'B'。'B' == 'B',匹配成功!退出while循环。 -
更新
j和b[i]:j++,j变为2。b[7] = 2。表示"ABACABAB"的最长相等前后缀长度是2("AB")。
看,我们正是通过 j = b[2],从长度为3的前缀 "ABA" 回退到了长度为1的前缀 "A",然后成功扩展到了 "AB"。这个 b[2] 中的索引 2,正是 j-1。
总结
-
j代表当前尝试匹配的前缀的长度。 -
当
s2[j]和s2[i]不匹配时,我们不能直接从j跳到0,那样效率太低。 -
我们利用已经计算好的
b数组,在s2[0...j-1](即已匹配的前缀) 中寻找它的最长相等前后缀。这个最长相等前后缀的长度就存储在b[j-1]里。 -
所以
j = b[j-1]的含义是:将当前尝试匹配的前缀,缩短为“已匹配前缀的最长相等前缀”,然后继续尝试匹配。
这样,我们既没有从头开始,又利用了之前匹配的结果,这正是KMP算法高效的精髓所在。希望这个解释能帮助你理解!
更多推荐


所有评论(0)