【题解】NOI2014动物园

传送门:洛谷P2375

一直到写到这道题目才发现我一直都理解了假的KMP……fail数组:fail[i]为从1-i(包含i)在内的字符串,相同的最长前后缀长度。

那么我们可以先思考暴力:先求出所有的fail,再不断往上跳,那么跳到的节点中(fail<<1)<i的个数即为num值。但这样的复杂度太高了,所以我们要进一步优化。

可以发现每一个节点所指向的fail节点是唯一的,但一个点可能是多个节点的fail,这是一个树形的关系。且在这个树形关系上,越靠近根节点的fail值也就越小。所以我们逐层标记Num值,直到找到符合条件的节点,则这个节点所标记的值就是它&它上方所有节点的个数。(若该点满足条件,则在它之上的一定满足)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 10000
#define ll long long
#define p 1000000007
int fail[maxn], num[maxn];
char s[maxn];
ll solve()
{
    ll ans = 1;
    int len = strlen(s + 1);
    memset(fail, 0, sizeof(fail));
    memset(num, 0, sizeof(num));
    num[1] = 1;
    for(int i = 2, j = 0; i <= len; i ++)
    {
        while(j && s[i] != s[j + 1]) j = fail[j];
        if(s[i] == s[j + 1]) j ++;
        fail[i] = j;
        num[i] = num[j] + 1; 
    }
    for(int i = 2, j = 0; i <= len; i ++)
    {
        while(j && s[i] != s[j + 1]) j = fail[j];
        if(s[i] == s[j + 1]) j ++;
        while((j << 1) > i) j = fail[j];
        ans = (num[j] + 1) * ans;
        ans %= p;
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --)
    {
        scanf("%s", s + 1); 
        printf("%lld\n", solve());
    }
    return 0;
}

 

posted @ 2018-02-07 16:56  Twilight_Sx  阅读(745)  评论(0编辑  收藏  举报