字符串算法三连击
本文是介绍KMP算法的运用
KMP算法
给定两个字符串 ,询问 是否在 中出现过,如果出现过要给出所有出现的位置,时间复杂度
KMP算法的核心是Next数组
Next 数组
定义 : 函数,它表示一个字符串的最长的严格前缀等于对应长度后缀的长度
,因为前缀和后缀都是 ,并且没有更长的了
,因为前缀和后缀都是 ,注意原串 也同时是前缀和后缀,但我们要求是严格前缀
,因为不存在一个前缀等于对应长度后缀
对于一个字符串 ,我们定义它的 数组的第 项为 前 个字符构成的前缀的 函数
对于字符串 来说,它的 数组是:
算法主体
如果要求 是否在 中出现过,先求 的 数组(假设已经求好)
记录两个指针 ,表示当前匹配到字符串 的位置,匹配到字符串 的 位置
如果 ,该字符匹配成功,
如果 ,匹配失败, ( 移动到可行的最后一个位置)
如果 到末尾,说明已经匹配成功,输出匹配位置,并,表示开始准备下一次匹配
求Next数组
核心思想:自己与自己错位做匹配
考虑已知 ,求
如果 ,那么
如果不相等?判断
for (int i = 2; i <= length; i++) { while (j > 0 && S[i] != S[j + 1]) j = next[j]; if (S[i] == S[j+1]) j++; next[i] = j; }
KMP在题目中的应用
下面是一些的题目
周期串判断
题目
给定一个字符串 ,问 是否是周期串,也就是是否有 这样的形式。如果有找出最短的循环节,否则输出 。
题解
考虑周期串有什么性质:如果 ( 个 连在一起),那么 既是 的前缀也是 的后缀,也就是 的长度应该等于
如果 是 的一个约数的话,根据前缀后缀的相等关系我们可以断定 必然是周期串,又由于 的最大性质,前 个字符一定就是最短循环节。否则 必然不是一个周期串。
CEOI 2011 Match
题目
给出一个长度为 的排列 ( 到 的所有数都出现一次),并给出一个长度为 的序列 ,求 的所有满足如下条件的子串:
- 该子串长度为
- 该子串的第 个数恰为整个子串的第 大的数
题解
修改一下 KMP 算法中, 中等号的定义.
定义 是 在 的前 位中的排名。
如果 的某个子串 中, 的排名恰好是 ,就认为。
可以用树状数组在将 离散化后 判定。
Q: 为什么这样做是对的呢?
考虑当前匹配的指针后移一位,不会影响之前的匹配结果
实际上就是把排名对应相等的字符串看成是等价的字符串做匹配
End.