「NOI 2014」动物园
看大多数人拿KMP做的,介绍一个更无脑的Z算法做法。
我们考虑不在 \(i\) 的位置计算 \(num_i\),而对于 \(s[1:i - j + 1] = s[j:i]\) 在 \(j\) 处统计对 \(i\) 的贡献,在固定 \(j\) 后,我们很容易发现跟Z算法类似,考虑求出 \(z_j\)。
有了 \(z\) 数组后我们能干什么呢?我们观察对于 \(j\) 来说合法的 \(i\) 满足什么条件,首先要满足 \(s[1:i - j + 1] = s[j:i]\),即 \(i \leq j + z_j - 1\),其次有前后缀不能重叠,即 \(i - j + 1 < j\),移项,\(i < 2j - 1\),所以 \(j\) 可以对所有 \(i \in [j, \min(j + z_j - 1, 2j - 2)]\) 产生 \(1\) 的贡献,区间加,求最终数组,差分即可。
/*
address:https://qoj.ac/problem/6234
AC 2026/7/24 10:58
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
char s[N];
int n;
int z[N];
inline void z_function(char* s, int n) {
z[1] = n;
int l = 1, r = 1;
for (int i = 2;i <= n;++i) {
z[i] = 0;
if (i < r) z[i] = min(r - i, z[i - l + 1]);
while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]]) ++z[i];
if (i + z[i] > r) l = i, r = i + z[i];
}
}
int num[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;cin >> T;
while (T--) {
for (int i = 1;i <= n + 1;++i) num[i] = 0;
cin >> (s + 1);
n = strlen(s + 1);
z_function(s, n);
for (int i = 1;i <= n;++i) {
int len = min(i - 1, z[i]);
++num[i];
--num[i + len];
}
for (int i = 1;i <= n;++i) num[i] += num[i - 1];
LL ans = 1;
for (int i = 1;i <= n;++i) ans = ans * (num[i] + 1) % mod;
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号