Z 函数(扩展 KMP)
约定:字符串下标以
对于个长度为
国外一般将计算该数组的算法称为 Z Algorithm,而国内则称其为 扩展 KMP。
这篇文章介绍在
样例¶
下面若干样例展示了对于不同字符串的 Z 函数:
z(\mathtt{aaaaa}) = [0, 4, 3, 2, 1] z(\mathtt{aaabaab}) = [0, 2, 1, 0, 2, 1, 0] z(\mathtt{abacaba}) = [0, 0, 1, 0, 3, 0, 1]
朴素算法¶
Z 函数的朴素算法复杂度为
1 2 3 4 5 6 7 8 | // C++ Version
vector<int> z_function_trivial(string s) {
int n = (int)s.length();
vector<int> z(n);
for (int i = 1; i < n; ++i)
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
return z;
}
|
1 2 3 4 5 6 7 8 | # Python Version
def z_function_trivial(s):
n = len(s)
z = [0] * n
for i in range(1, n):
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
z[i] += 1
return z
|
线性算法¶
如同大多数字符串主题所介绍的算法,其关键在于,运用自动机的思想寻找限制条件下的状态转移函数,使得可以借助之前的状态来加速计算新的状态。
在该算法中,我们从
对于
算法的过程中我们维护右端点最靠右的匹配段。为了方便,记作
在计算
- 如果
i\le r [l,r] s[i,r] = s[i-l,r-l] z[i]\ge \min(z[i-l],r-i+1) - 若
z[i-l] < r-i+1 z[i] = z[i-l] - 否则
z[i-l]\ge r-i+1 z[i] = r-i+1 z[i]
- 若
- 如果
i>r s[i] z[i] - 在求出
z[i] i+z[i]-1>r [l,r] l=i, r=i+z[i]-1
可以访问 这个网站 来看 Z 函数的模拟过程。
实现¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // C++ Version
vector<int> z_function(string s) {
int n = (int)s.length();
vector<int> z(n);
for (int i = 1, l = 0, r = 0; i < n; ++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] < n && s[z[i]] == s[i + z[i]]) ++z[i];
}
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
return z;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # Python Version
def z_function(s):
n = len(s)
z = [0] * n
l, r = 0, 0
for i in range(1, n):
if i <= r and z[i - l] < r - i + 1:
z[i] = z[i - l]
else:
z[i] = max(0, r - i + 1)
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
z[i] += 1
if i + z[i] - 1 > r:
l = i
r = i + z[i] - 1
return z
|
复杂度分析¶
对于内层 while
循环,每次执行都会使得
对于外层循环,只有一遍线性遍历。
总复杂度为
应用¶
我们现在来考虑在若干具体情况下 Z 函数的应用。
这些应用在很大程度上同 前缀函数 的应用类似。
匹配所有子串¶
为了避免混淆,我们将
为了解决该问题,我们构造一个新的字符串
首先计算
其时间复杂度(同时也是其空间复杂度)为
本质不同子串数¶
给定一个长度为
考虑计算增量,即在知道当前
令
设串
所以,将字符
算法时间复杂度为
值得注意的是,我们可以用同样的方法在
字符串整周期¶
给定一个长度为
考虑计算
该事实的证明同应用 前缀函数 的证明一样。
练习题目¶
- CF126B Password
- UVA # 455 Periodic Strings
- UVA # 11022 String Factoring
- UVa 11475 - Extend to Palindrome
- LA 6439 - Pasti Pas!
- Codechef - Chef and Strings
- Codeforces - Prefixes and Suffixes
本页面主要译自博文 Z-функция строки и её вычисление 与其英文翻译版 Z-function and its calculation。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:LeoJacob, Marcythm, minghu6
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用