接着,我们尝试进行暴力匹配,如果 $s_{i+z_i}$ 与 $s_{z_i+1}$ 相同,就让 $z_i$ 加 $1$。
暴力匹配完后更新 $l,r$ 即可。
Z 函数的复杂度是 $O(n)$ 的,因为每个字符至多被暴力匹配 $1$ 次。
求解 $z$ 数组的代码:
```cpp
z[1] = m; // 完全匹配
for (int i = 2, l = 0, r = 0; i <= m; i++) {
if (i <= r) z[i] = min (z[i - l + 1], r - i + 1); // 获取初值
while (i + z[i] <= m && b[i + z[i]] == b[z[i] + 1]) z[i] ++; // 暴力匹配
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; // 更新 l, r
}
```
同样,我们定义 $p_i$ 为为字符串 $b$ 与字符串 $a$ 从第 $i$ 位开始的后缀的 LCP 长度。
则类似的,可以得到求解 $p_i$ 的代码:
```cpp
for (int i = 1, l = 0, r = 0; i <= n; i++) {
if (i <= r) p[i] = min (z[i - l + 1], r - i + 1);
while (i + p[i] <= n && a[i + p[i]] == b[p[i] + 1]) p[i] ++;
if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
cnt[p[i]] ++; // 统计答案
}
```
对于每个询问,若询问长度为 $x$,则输出 $cnt_x$。
完整代码:
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
int n, m, q, z[N], p[N], cnt[N];
char a[N], b[N];
int main () {
scanf ("%d %d %d", &n, &m, &q);
scanf ("%s", a + 1);
scanf ("%s", b + 1);
z[1] = m;
for (int i = 2, l = 0, r = 0; i <= m; i++) {
if (i <= r) z[i] = min (z[i - l + 1], r - i + 1);
while (i + z[i] <= m && b[i + z[i]] == b[z[i] + 1]) z[i] ++;
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
for (int i = 1, l = 0, r = 0; i <= n; i++) {
if (i <= r) p[i] = min (z[i - l + 1], r - i + 1);
while (i + p[i] <= n && a[i + p[i]] == b[p[i] + 1]) p[i] ++;
if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
cnt[p[i]] ++;
}
while (q--) {
int x; scanf ("%d", &x);
printf ("%d\n", cnt[x]);
}
return 0;
}
```
本文没有图解,对初学者来说可能比较抽象。想要更好理解 Z 函数的同学可以前往 [P5410 【模板】扩展 KMP/exKMP(Z 函数)](https://www.luogu.com.cn/problem/P5410) 的题解区学习。
大家有任何建议或疑惑都可以在评论中提出,感谢!