洛谷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;
}