KMP算法是什么
是用来解决字符串匹配的问题。在一个 $str$ 字符串中找到一个子字符串 $sub$,返回索引的位置。
常规能想到的最简单的方式就是一个一个比对,匹配失败的话就向下挪一个位置继续匹配。这样做时间复杂度是 $O(n^2)$。
KMP算法可以在 $O(n)$ 的时间复杂度内解决这个问题。
next数组是什么
$next$ 数组的本质是寻找子串中“相同前后缀的长度,并且最长”。并且不能是字符串本身。举个例子而言,如下所示:
A | B | A | B | C |
---|---|---|---|---|
0 | 0 | 1 | 2 | 0 |
第 $1$位 $A$ 只有一个字符,不能是字符串本身,为 $0$;
第 $2$位 $B$ 和 $A$ 比较,不相等,为 $0$;
第 $3$位 $A$ 和 $A$ 比较,相等,所以相同前缀为 $1$;
第 $4$位 $B$ 和前缀为 $1$ 后的 $B$ 比较,相等,和前缀组成了 $AB$ 的相同模式的串,所以相同前缀为 $2$;
第 $5$ 位 $C$ 和前缀为 $2$ 后的 $A$ 比较,不相等,和前缀长度为 $0$ 的比,相同前缀为$0$;
关于最长前缀的理解是,如下图所示。
最后一位的 $A$ 很显然可以和 $ABA$ 组成长度为 $3$ 的相同前缀,也可以自己 $A$ 组成长度为 $1$ 的相同前缀。这种情况下选择较大的那个值。
求解next数组
$next$ 数组求解的本质是动态规划。假设我们已经知道当前数字的共同前后缀了,如下图所示。
接下来求第 $i$ 位的 $next$ 数组,分为两种情况。
- 下一位也和前缀相等
那就很明显等于 $next[i - 1]$ 加上 $1$。 - 下一位和前缀不相等
这里不是直接等于 $0$ 哦。如下图所示
虽然 $ABAB$ 和 $ABAC$ 并不相等,但是也不能直接设为 $0$。因为这个新来的 $B$ 可以和前面的 $A$ 组成 $AB$,达到 $2$ 的长度。那么这个数难道要暴力求解吗?其实不然。此时第 $i - 1$ 位的 $next$ 的可利用的值在前缀的 $next$ 数组中可以得到答案,在本图中为 $next[2]$,即是 $1$。此时修改前缀长度为 $next[2]$,则又回到了最开始的状况,匹配长度为 $1$ 并且匹配下一位,下一位 $B$ 相等,所以 $next[i]$ 为 $1 + 1 = 2$。
代码如下:
1 | vector<int> buildNext(string sub) { |
next数组在KMP中的应用
KMP算法在匹配失败的时候,回去看最后失败的字符前一个字符所对应的 $next$ 值。$next$ 数组中的值是什么作用呢?它代表我们可以“跳过匹配”的字符数量。
这样就可以保证匹配的 $i$ 不向后退,达到时间复杂度 $O(n)$ 的目的。
代码如下:
1 | int kmp(string s, string sub) { |