kmp 算法

· · 个人记录

kmp 算法用于在线性时间内求字符串的前缀最长 border,或求解字符串匹配问题。

定义字符串 s 的 border 为字符串 t,满足 ts 的前缀也是 s 的后缀,且 t 不是 s 本身。

考虑现在给定两个字符串 s,t,求 ts 出现的所有位置。

以下默认字符串下标从 1 开始。

pre_it 的长度为 i 的前缀。

首先预处理数组 pp_i 表示 pre_i 的最长 border 的长度。

这里数组 p 就是所谓的 next 数组。

初始化 p_1=0

考虑现在求 p_i,令 j=p_{i-1}

1.若 t_i=t_{j+1},则 p_i=j+1

2.否则若 j=0,则 p_i=0

3.否则,考虑一个 x 满足 x<j,并且 pre_{x+1}pre_i 的 border。

那么一定有 pre_xpre_{i-1} 的 border。

又因为 x<j,且 pre_jpre_{i-1} 的 border,所以 pre_xpre_j 的 border。

所以满足条件的最大的 x 就是 p_j

所以可以 j\leftarrow p_j,然后返回第 1 步。

考虑这样做的时间复杂度,显然 j 每次最多 +1,所以 j 减少的次数也不超过 |t| 次,所以复杂度为 O(|t|)

再考虑如何利用 p 数组求 ts 中出现的所有位置。

考虑从前到后依次加入 s 的字符。

定义一个变量 j,表示最大的 x 满足当前已经加入的 s 的部分的长度为 x 的后缀和 pre_x 相同。

初始化 j=0

考虑加入 s_i,若 s_i=t_{j+1},则 j\leftarrow j+1

j=|t|,则 s 的长度为 i 的前缀的长度为 j 的后缀和 t 相同,找到了一个 ts 中出现的位置,为 i-|t|+1

s_i\neq t_{j+1},则 j\leftarrow j+1,重复此步骤直到 s_i=t_{j+1}j=0

容易发现这部分的做法和预处理 p 数组时类似,所以正确性不再赘述,复杂度 O(|s|)

总复杂度 O(|s|+|t|)

模板题:P3375 【模板】KMP字符串匹配 提交记录

习题:

P3435 [POI2006]OKR-Periods of Words

P2375 [NOI2014] 动物园

P4824 [USACO15FEB]Censoring S

CF1017E The Supersonic Rocket

P3193 [HNOI2008]GT考试

P3426 [POI2005]SZA-Template

P5829 【模板】失配树