exKMP

发布时间 2023-07-30 16:36:41作者: 我是浣辰啦

概念

对于一个长度为 \(n\) 的字符串 \(s\)。定义函数 \(z_i\) 表示 \(s\)\(s[i, n - 1]\) 的最长公共前缀的长度, \(z\) 被称为 \(s\) 的Z函数

特别地, \(z_0 = 0\)

实现

对于 \(i\) ,我们称区间 \([i, i + z[i] - 1]\)\(i\) 的匹配段,亦称Z-box

考虑维护右端点最靠右的匹配段,记作 \([l, r]\) 。根据定义, \(s[l, r]\)\(s\) 的前缀。在计算 \(z_i\) 时我们保证 \(l \leq i\) ,初始时 \(l = r = 0\)

过程:

  • \(i \leq r\) ,则有 \(s[i, r] = s[i - l, r - l]\) ,因此 \(z_i \geq \min(z_{i - l}, r - i + 1)\) ,此时
    • \(z[i - l] < r - i + 1\)\(z_i = z_{i - l}\)
    • 否则, \(z_{i - l} \geq r - i + 1\) ,令 \(z_i = r - i + 1\) 然后暴力枚举下一位匹配到无法拓展为止
  • \(i > r\) ,那么直接暴力枚举下一位匹配到无法拓展为止
inline void GetFunctionZ(string str) {
	for (int i = 1, l = 0, r = 0; i < str.length(); ++i) {
		if (i <= r && z[i - l] < r - i + 1)
			z[i] = z[i - l];
		else {
			z[i] = max(0, r - i + 1);
			
			while (i + z[i] < str.length() && str[i + z[i]] == str[z[i]])
				++z[i];
		}
		
		if (i + z[i] - 1 > r)
			l = i, r = i + z[i] - 1;
	}
}