【BZOJ3670】【codevs3319】动物园,KMP+时间优化

传送门1
传送门2
写在前面:难得题面和题解是一致的算法
思路:原始想法是先KMP建next数组,同时记录1-len每个长度的“公共前缀后缀”的总和sum[i],很容易想到 sum[i]=1+sum[tmp] ,tmp就是长度为i的字符串中是前缀同时是后缀的字符串的长度(就是那个用next[next[…求的东西),之后判断长度1-len的next[i],如果next<i/2, num[i]=sum[next[i]] ,不然就令next[i]=next[next[i]],继续判断,直到符合条件为止。
这种最简单粗暴的方式可以过50分,但我们发现如果原串是一个循环字符串而且循环节特别小,例如“aaaaaaaaaaa…”,那么每次求num时,next[i]是一个字符一个字符往前蹦达的,那也就是说最坏情况下时间复杂度会达到 O(len2) ,爆炸。(顺便说一下,蒟蒻就是被卡在这里好久,想到这个问题就头大,搞的忘记了next数组的性质,直接转移到next[i/2]上去了……竟然还能过样例)无法,蒟蒻选择看题解,发现题解的做法是把求next,sum放在一起,求答案(num)放在一起,开了两个循环,后来想了一下,求next,sum时tmp是从next[i]开始逐渐往后跳,时间复杂度均摊下来是 O(len) ,而求答案时完全可以类似于从前往后走,每次求num时前进最多一格,后退不确定,但总的复杂度是一定的且可以接受的。
注意:答案用long long保存,sum[1]=1(它自身就是一个哦)

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int tmp,T,len;
int sum[1000010],next[1000010];
long long ans;
char s[1000010];
main()
{
    sum[1]=1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",s);
        ans=1;tmp=0;
        for (int i=1;s[i];i++)
        {
            tmp=next[i];
            while (tmp&&s[tmp]!=s[i]) tmp=next[tmp];
            tmp+=(s[i]==s[tmp]);
            next[i+1]=tmp;
            sum[i+1]=sum[tmp]+1;
        }
        for (int i=1;s[i];i++)
        {
            while (tmp&&s[tmp]!=s[i]) tmp=next[tmp];
            tmp+=(s[i]==s[tmp]);
            while (tmp>((i+1)>>1)) tmp=next[tmp];
            ans=ans*(sum[tmp]+1)%mod;
        }
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值