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