洛谷P2375 动物园

洛谷P2375 动物园

这一题我认为巨难
有一个很简单的暴力做法50分:

#include<bits/stdc++.h>
using namespace std;
int n;
char s[1000010];
int next[1000010];
int cnt[1000010];
long long ans;
int main()
{
    scanf("%d",&n);
    while (n--){
        memset(next,0,sizeof(next));
        memset(cnt,0,sizeof(cnt));
        ans=1;
        scanf("%s",s+1);
        int len=strlen(s+1);
        next[0]=-1;
        next[1]=0;
        cnt[1]=1;
        for (int i=2;i<=len;i++){
            int p=next[i-1];
            while ((p!=-1) && (s[p+1]!=s[i]))	p=next[p];
            p++;
            next[i]=p;
            if (p!=0) cnt[i]=cnt[p]+1;
            else cnt[i]=1;
        }
        for (int i=1;i<=len;i++){
            int p=next[i];
            while (p>i/2) p=next[p];
            ans=ans*(cnt[p]+1) %1000000007;	
        }
        printf("%lld\n",ans);
    }
    return 0;
}

然后我们发现TLE
怎么优化呢?
我们发现,每次执行while (p>i/2) p=next[p];时,递归次数太多了
所以我们可以减少递归次数
我们不妨不用每次让p返回到next[i],留在上个位置即可
留在哪里呢
不妨让它留在上一次的位置
为什么呢?
因为

pi<=pi-1+1

证明:

假设第i−1次循环得到j为j,这次(第i次)最终得到的为j+2
那么可以发现前缀i的前缀j+2=他长度为j+2的后缀
那么前缀j+2=前缀i的长度为j+2的 后缀的长度为j+1的 前缀(结束为i−1)
又因为我们得到的j+2满足不重叠,那么j+1自然也不会重叠
那么我们可以得到前缀i−1的的最优j为j+1 这样就冲突啦,于是这是不可能的

绕到死(我也没有完全想通),没办法
100分代码:

#include<bits/stdc++.h>
using namespace std;
int n;
char s[1000010];
int next[1000010];
int cnt[1000010];
long long ans;
int main()
{
    scanf("%d",&n);
    while (n--){
        memset(next,0,sizeof(next));
        memset(cnt,0,sizeof(cnt));
        ans=1;
        scanf("%s",s+1);
        int len=strlen(s+1);
        next[0]=-1;
        next[1]=0;
        cnt[1]=1;
        for (int i=2;i<=len;i++){
            int p=next[i-1];
            while ((p!=-1) && (s[p+1]!=s[i]))	p=next[p];
            p++;
            next[i]=p;
            if (p!=0) cnt[i]=cnt[p]+1;
            else cnt[i]=1;
        }
        int p=0;
        for (int i=2;i<=len;i++){
			while ((p!=0) && (s[p+1]!=s[i]))	p=next[p];
			if (s[p+1]==s[i]) p++;
            while (p*2>i) p=next[p];
            ans=ans*(cnt[p]+1) %1000000007;	
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值