P2375 [NOI2014] 动物园

P2375 [NOI2014] 动物园

题意是对于每个前缀,求前缀后缀不交的且前后缀相等的前后缀数量数组,\(1\le len \le 10^6\)

考虑先求出正常的 \(next\) 数组,KMP \(O(len)\) 求解。

对于 \(next'\) 数组,可以由前一个数的 \(next'\) 数组转移,如果新数大小超过了 \(\frac{i}{2}\) 就跳到前一个 \(next\) 数组。因为如果现在已经超出长度限制,加 \(1\) 后一定超出,所以这么做是正确的。

记录 \(num\) 只需要在过程中递推一下就好了。详见代码。

时间复杂度 \(O(len)\)

#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
using namespace std;
typedef long long ll;
const int N = 1e6 + 7, mod = 1e9 + 7;
ll ans;
int n,l;
char c[N];
int nex[N],num[N];
void getans () {
    num[1] = 1;
    int j = 0;
    rep(i, 1, l - 1) {
        while(j && c[i] != c[j]) j = nex[j];
        if(c[i] == c[j]) j++;
        nex[i + 1] = j; num[i + 1] = num[j] + 1;
    }
    j = 0;
    rep(i, 1, l - 1) {
        while(j && (c[i] != c[j])) j = nex[j];
        if(c[i] == c[j]) j++;
        while((j<<1) > (i + 1) ) j = nex[j];
        ans = ans * (num[j] + 1) % mod;
    }
}
int main () {
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("my.out", "w", stdout);
    #endif
    sf("%d" , &n );
    while (n--) {
        memset(nex, 0, sizeof(nex));
        memset(num, 0, sizeof(num));
        ans = 1;
        sf("%s", c);
        l = strlen(c);
        getans();
        pf("%lld\n", ans);
    }
}
posted @ 2024-09-27 13:29  liyixin  阅读(6)  评论(0编辑  收藏  举报