扩展 KMP(Z 函数)

扩展 KMP(Z 函数)

下文用 \([a, b]\) 表示 \(s[a \to b]\)\((l, r)\) 表示当前 \(r\) 最右的匹配段。

问题一

要解决的问题为:求出 \(z\) 函数,\(z(i) = \operatorname{LCP}(s[i, nS], s[1, nS])\),其中 \(\operatorname {LCP}(a, b)\) 表示 \(a, b\) 的最长相同前缀。

考虑到 \([l, r] = [1, r - l +1]\) 所以 \(\forall i \in [l, r]\)\([i, r] = [i - l + 1, r - l + 1]\)

因此,求 \(z(i)\) 可以利用到 \(z(i - l + 1)\),即 \(z(i) \geq \min\{ z(i - l + 1), r - i + 1\}\)

分类讨论:

  1. \(i \leq r\)
    1. \(z(i - l + 1) < r - i + 1\)\(z(i) = z(i - l + 1)\)
    2. \(z(i - l + 1) \geq r - i + 1\) 则令 \(z(i) = r - i + 1\) 然后往后扩展。
  2. \(i > r\) 则令 \(z(i) = 0\) 然后向后扩展。

由此可以写出代码:

nS = strlen(s + 1);
l = 0, r = 0;
for (int i = 2; i <= nS; ++ i)
{
	if (i <= r && z[i - l + 1] < r - i + 1)
		z[i] = z[i - l + 1];
	else 
	{
		z[i] = std::max(r - i + 1, 0);
		while (i + z[i] <= nS && s[1 + z[i]] == s[i + z[i]])
			z[i] ++;
	}
	if (i + z[i] - 1 > r)
		l = i, r = i + z[i] - 1;
}

显然,外层循环只会循环 \(nS\) 次,而内层 while 循环每循环一次必使得 \(r\) 增大,\(r\) 顶多增大到 \(nS\),故内层循环顶多进行 \(nS\) 次。

综上所述,时间复杂度为 \(\mathcal O(n)\)

问题二

我们要解决的问题为:求 \(b\)\(a\) 每一个后缀的 \(\operatorname{LCP}\)

\(b + a\) 拼起来做就可以了。

posted @ 2022-07-30 15:28  chzhc  阅读(45)  评论(0编辑  收藏  举报
levels of contents