kmp 算法的核心是计算 Next 数组,但是网上流传的实现代码比较晦涩,这里用动态规划来计算 Next 更加容易理解。
#### 预先定义
P[] 为长度为 n 的匹配字符串,index 从 0 开始
Next[] 为要计算的数组,Next[j] 表示长度为 j 的字符串"P[0]P[1]...P[j-1]"的前缀等于后缀的最长长度,比如对于字符串 P 为"abCdabCe"来说其 Next[7]为 3 因为 abC 的长度为 3, 边界值 Next[0] = 0, Next[1] = 0
动态规划状态函数为 f(int j, char x),f(j,x)表示长度为 j+1 的字符串"P[0]P[1]...P[j-1]x"
的前缀等于后缀的最长长度,比如对于字符串 P 为"abCdabCe"来说其 f(6, 'C')为 3 因为 abC 的长度为 3,很直接的我们可以知道 Next[j] = f(j-1, P[j])
#### 状态转移方程的三个情况
f(j, x) =
1. 当 x 等于 P[Next[j]+1] 时,f(j, x) = Next[j] + 1
2. 当 x 不等于 P[Next[j]+1] , 且 j 为 0 时, 这就是边界条件,此时 f(j, x) = 0
3. 当 x 不等于 P[Next[j]+1] , 且 j 不为 0 时,f(j, x) = f(Next[j], x)
#### 额外的解释说明
上面条件 1 与 2 很好理解,这里需要对条件 3 稍作解释。
我们先定义一个字符串上的操作 K,
使 K(P) = Next[len(P)],也就是对 P 取最长前缀后缀相同长度。
对于匹配字符串 P"eaabCaeae"来说 Next[8]=2 因为"ea"长度为 2,当我们需要计算 Next[9]也就是 f(8, e)时 由于字符 e 不等于字符 a,所以条件 1 不满足,而由于我们知道 Next[8]是代表字符串"ea"是长度为 8 的子字符串"eaabCaea"的最长相同前缀与后缀长度,这里为了方便说明,我们给这两个前缀后缀分别命名为 pre8 与 suf8,pre8 与 suf8 都是长度为 2 的字符串"ea", 我们要计算 Next[9]也就是 f(8, e)也就是相当于求新后缀字符串 "${suf8}e"的 K, 这是因为最末字符 e 的存在,如果可求 K 大于 0 那么另一个字符 e 必然也存在于 suf8 中,而 K("${suf8}e") 等于 K("${pre8}e") 等于 f(len(pre8), e) 等于 f(Next[8], e)
参考
https://www.cnblogs.com/dusf/p/kmp.html
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
https://www.v2ex.com/t/753507
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.