NOI2014 动物园

传送门

KMP好题啊……

题中要求我们求出长度不超过原字符串一般的相同的前后缀的个数。其实这个用做前面几道题的思路大致猜测一下……可以发现,我们只要从这个字符串一直往它的next递归,那么我们就可以获得一系列的公共前后缀,而且只要当next的长度<=原长度的一半的时候答案即合法。(不能继续递归,否则就重复计算了)

每个点可以获取的相同的前后缀个数可以通过kmp预处理出来,我们只要让ans[i] = ans[j]+1,(其中next[i] = j),因为在i向j跳的时候相当于获取了一个长度为j的公共前后缀的贡献。

不过我们如果这样一直递归,遇到无良数据(比如200000个a),你就T飞了,因为每次递归只会减少1的长度。解决的方法是,我们在计算的时候也像KMP一样,每次不更新j的位置,使之一直呆在小于i的一半的位置(因为你肯定是要小于i的一半才合法),这样可以避免许多无用的递归,就可以在线性时间之内求解了。

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 10005;
const int N = 1000005;
const ll mod = 1000000007;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int n,nxt[N],len;
char s[N];
ll cur,ans[N];

void getnext()
{
    int j = 0;
    rep(i,2,len)
    {
    while(j && s[j+1] != s[i]) j = nxt[j];
    if(s[j+1] == s[i]) j++;
    nxt[i] = j,ans[i] = ans[j] + 1;
    }
}

void clear()
{
    memset(nxt,0,sizeof(nxt));
    memset(ans,0,sizeof(ans));
    cur = 1;
}

int main()
{
    n = read();
    while(n--)
    {
    clear();
    scanf("%s",s+1),len = strlen(s+1);
    ans[1] = 1,getnext();
    int j = 0;
    //rep(i,0,len-1) printf("%d ",nxt[i]);enter;
    //rep(i,0,len-1) printf("%lld ",ans[i]);enter;
    rep(i,2,len)
    {
        while(j && s[i] != s[j+1]) j = nxt[j];
        if(s[j+1] == s[i]) j++;
        while(j << 1 > i) j = nxt[j];
        //  printf("!%d\n",j);
        cur *= (ans[j] + 1),cur %= mod;
    }
    printf("%lld\n",cur);
    }
    return 0;
}

 

posted @ 2018-10-10 23:57  CaptainLi  阅读(114)  评论(0编辑  收藏  举报