本文将从原理上详细解释KMP算法中的next数组以及nextval数组,尽量让大家明白它们到底在记录什么,为什么要这样算。以及现在普遍的KMP算法实现当中的next数组与前两者有何不同。篇幅较长,但尽量讲清楚。
文章目录
next数组
next数组到底在记录什么?
虽然数据结构中对next数组有定义,但并不易于理解,因此我个人对next数组进行了一个简单解释:
next数组指示了当前模式串在该位置匹配冲突(即失配,个人习惯称冲突)时,应该将模式串的哪一位与此位对齐。
这里为刚学习KMP的朋友解释一下,KMP算法进行字符串匹配时,被检索的串称为“文本串”,要检索的目标串称为“模式串”。next数组与nextval数组的求解只与模式串有关。
举个例子:
假设需要在字符串 “abaabbcabaabcac” 中查找是否存在字符串 “abaabcac”,那么 "abaabbcabaabcac"就是文本串,"abaabcac"就是模式串。
这里其它的许多详解中会直接告诉你,next数组的第一位是0,第二位是1,然后从第三位开始继续算什么的,但很少看到有解释为什么第一位一定是0,第二位一定是1。
为了解释这个问题,我们首先要搞清楚模式串的“位”是什么样的。
对“abaabcac”来说,第一个字符a就是第一位,不要受一些编程语言思维的影响认为这是第0位。
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
模式串 | a | b | a | a | b | c | a | c |
但是为了便于理解,我们假想模式串的最前面还有一个空白字符位,称为第0位,即:
位数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
模式串 | a | b | a | a | b | c | a | c |
现在,我们再来对照前面给出的定义:
next数组指示了当前模式串在该位置匹配冲突时,应该将模式串的哪一位与此位对齐。
是不是就理解为什么第一位一定是0,第二位一定是1了?
还没有?那我们再假想一个文本串加上来。
待求位: 1 2 3 4 5 6 7 8
文本串: x x x x x x x x x x x x x x x x
模式串: a b a a b c a c
现在标红的位置发生冲突,那么下一步应该让模式串的哪一位跟当前冲突的 x 位对齐呢?没错,就是第0位。也就是变成:
待求位: 1 2 3 4 5 6 7 8
文本串: x x x x x x x x x x x x x x x x
模式串: a b a a b c a c
位数: 0 1 2 3 4 5 6 7 8
所以,当模式串的第一位发生冲突时,应该将模式串的第0位与当前位置对齐,也就是next[1]=0;
继续看第二位发生冲突的情况:
待求位: 1 2 3 4 5 6 7 8
文本串: a x x x x x x x x x x x x x x x
模式串: a b a a b c a c
此时需要将模式串移动为:
待求位: 1 2 3 4 5 6 7 8
文本串: a x x x x x x x x x x x x x x x
模式串: a b a a b c a c
位数: 0 1 2 3 4 5 6 7 8
所以,当模式串的第二位发生冲突时,应该将模式串的第1位与当前位置对齐,也就是next[2]=1;
无论模式串如何变化,只要其长度大于等于2,这两种情况都是固定的,因此next[1]一定为0,next[2]一定为1。
继续看第三位发生冲突的情况:
待求位: 1 2 3 4 5 6 7 8
文本串: a b x x x x x x x x x x x x x x
模式串: a b a a b c a c
这时,假如我们仍然只移