Loading

P2375 题解

我们考虑朴素算法。

显然,我们可以先跑一遍 KMP,计算出每个 \(i\)\(nxt_i\)

然后,容易发现我们可以暴力跳每一个前缀串的 border,这样可以直接统计出 border 长度 \(\leq \lfloor \frac{i}{2} \rfloor\) 的 border 数量。


回到原题。我们发现上述朴素做法会被 \(2 \times 10^5\)\(\text{a}\) 拉爆。于是我们考虑优化。

我们能不能考虑把重复的计算节省掉呢?

考虑类似 KMP 的优化。假设当前我们计算到 \(i\)

发现,如果我们最大的 \(\leq \lfloor \frac{i}{2} \rfloor\) 的 border 长度为 \(x_i\),则 \(x_i\) 一定是 \(x_{i - 1}\) 的一个 border。

证明:博主暂时是感性理解。后面补严禁证明。

于是考虑一直使用一个 \(j\)。复杂度为 \(\mathcal{O}(n)\)

以下是代码实现:

/*******************************
| Author:  DE_aemmprty
| Problem: P1001 A+B Problem
| Contest: Luogu
| URL:     https://www.luogu.com.cn/problem/P1001
| When:    2023-12-02 15:30:35
| 
| Memory:  512 MB
| Time:    1000 ms
*******************************/
#include <bits/stdc++.h>
using namespace std;

int read() {
    char c = getchar();
    int x = 0, p = 1;
    while ((c < '0' || c > '9') && c != '-') c = getchar();
    if (c == '-') p = -1, c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * p;
}

const int N = 1e6 + 7;

int n;
string s;
int nxt[N], num[N], q[N];

void solve() {
    cin >> s;
    n = (int) s.size(); s = " " + s;
    q[1] = 1;
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && s[j + 1] != s[i]) j = nxt[j];
        if (s[j + 1] == s[i]) j ++;
        nxt[i] = j; q[i] = q[j] + 1;
    }
    long long ans = 1;
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && s[j + 1] != s[i]) j = nxt[j];
        if (s[j + 1] == s[i]) j ++;
        while (j * 2 > i) j = nxt[j];
        (ans *= 1ll * (q[j] + 1)) %= 1000000007;
    }
    printf("%lld\n", ans);
}

signed main() {
    int t = read();
    while (t --) solve();
    return 0;
}
posted @ 2024-04-01 19:24  DE_aemmprty  阅读(13)  评论(0)    收藏  举报