「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;
}
posted @ 2026-07-24 15:37  keysky  阅读(2)  评论(0)    收藏  举报