P2375 [NOI2014] 动物园

P2375 [NOI2014] 动物园

题目描述

近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。

某天,园长给动物们讲解 KMP 算法。

园长:“对于一个字符串 S,它的长度为 L。我们可以在 O(L) 的时间内,求出一个名为 next 的数组。有谁预习了 next 数组的含义吗?”

熊猫:“对于字符串 S 的前 i 个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作 next[i]。”

园长:“非常好!那你能举个例子吗?”

熊猫:“例 Sabcababc,则 next[5]=2。因为S的前5个字符为 abcabab 既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出 next[1]=next[2]=next[3]=0next[4]=next[6]=1next[7]=2next[8]=3。”

园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在 O(L) 的时间内求出 next 数组。

下课前,园长提出了一个问题:“KMP 算法只能求出 next 数组。我现在希望求出一个更强大 num 数组一一对于字符串 S 的前 i 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作 num[i]。例如 Saaaaa,则 num[4]=2。这是因为S的前 4 个字符为 aaaa,其中 aaa 都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而 aaa 虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,num[1]=0,num[2]=num[3]=1,num[5]=2。”

最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出num数组呢?

特别地,为了避免大量的输出,你不需要输出 num[i] 分别是多少,你只需要输出所有 (num[i]+1) 的乘积,对 109+7 取模的结果即可。

数据范围

n5,L1,000,000

Solution:

首先我们注意到 , next 数组的定义是在 s[1,i] 这个字符串中既是 s[1,i] 的前缀,又是其后缀的最长字符串的长度

然后我们要求的 nums[1,i] 中前后缀相等且长度不超过一半的前后缀个数。我们可以先考虑对于不限制长度的前后缀相等的前后缀计数。

cnti 表示无长度限制时 s[1,i] 的所有前后缀中前后缀相等的个数。我们会发现 cnti 其实等价于从 i 往前跳 next 数组,跳几次之后会为 0。

我们思考为什么:

图中红色表示 s[1,i] 这个字符串,前后两段绿色部分相同,四段蓝色部分相同。

我们把图画出来了之后,不难发现这样做是能保证每次跳到的 next 都能保证 s[1,nxt] 既是前缀又是后缀,然后由于 next 的定义又是 既是其前缀,又是其后缀的最长字符串的长度。我们就能保证这样不会算漏。

那么我们如何保证答案符合不超过一半长度的限制呢? 回顾一下我们求 next 时的过程,用一个辅助指针 j 来表示当前能和哪一位匹配,类似的,我们在答案统计时对于长度做这样的一个限制,首先不断地跳 nxet 直到 s[j+1]=s[i] 或者 j=0,然后再看与当前后缀匹配的前缀 s[1,j+1] 的长度是否超过 s[1,i] 长度的一半,若超过,则继续往前跳 next.正确性见前文。

Code:

#include<bits/stdc++.h>
#define ll long long
const int N=1e6+6;
const int mod=1e9+7;
using namespace std;
ll mul(ll x,ll y){return x*y%mod;}
ll nxt[N],cnt[N],n;
char c[N];
ll ans;
void work()
{
scanf("%s",c+1);
n=strlen(c+1);
cnt[1]=1;
ans=1;
int j=0;
for(int i=0;i<=n;i++)nxt[i]=0;
for(int i=2;i<=n;i++)
{
while(j&&c[j+1]!=c[i])j=nxt[j];j+=(c[j+1]==c[i]);
nxt[i]=j;cnt[i]=cnt[j]+1;
}
j=0;
for(int i=2;i<=n;i++)
{
while(j&&c[j+1]!=c[i])j=nxt[j];j+=(c[j+1]==c[i]);
while((j<<1)>i)j=nxt[j];
ans=mul(ans,cnt[j]+1);
}
printf("%lld\n",ans);
}
int main()
{
//freopen("P2375.in","r",stdin);freopen("P2375.out","w",stdout);
int T;cin>>T;
while(T--)work();
return 0;
}
posted @   liuboom  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示