对于一个长度为 n 的字符串 s,定义函数 z[i] 表示 s 和 s[i,n−1](即以 s[i] 开头的后缀)的最长公共前缀(LCP)的长度,则 z 被称为 s 的 Z 函数。特别地,z[0]=0。
国外一般将计算该数组的算法称为 Z Algorithm,而国内则称其为 扩展 KMP(exKMP)。
这篇文章介绍在 O(n) 时间复杂度内计算 Z 函数的算法以及其各种应用。
解释
下面若干样例展示了对于不同字符串的 Z 函数:
z(aaaaa)=[0,4,3,2,1]
z(aaabaab)=[0,2,1,0,2,1,0]
z(abacaba)=[0,0,1,0,3,0,1]
朴素算法
Z 函数的朴素算法复杂度为 O(n2):
实现
[list2tab]
C++
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;}
Python
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
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;}
Python
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 循环,每次执行都会使得 r 向后移至少 1 位,而 r<n−1,所以总共只会执行 n 次。