一开始的方向应该对了,但是没有想到合理的优化还是没写出来……

题目描述

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

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

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

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

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

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

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

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

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

特别地,为了避免大量的输出,你不需要输出\(num[i]\)分别是多少,你只需要输出\(\prod_{i=1}^L (num[i]+1)\),对\(1,000,000,007\)取模的结果即可。

其中\(\prod_{i=1}^n (num[i]+1)=(num[1]+1)\times (num[2]+1)\times \dots \times (num[n]+1)\)。

输入输出格式

输入格式:

第\(1\)行仅包含一个正整数\(n\) ,表示测试数据的组数。

随后\(n\)行,每行描述一组测试数据。每组测试数据仅含有一个字符串\(S\),\(S\)的定义详见题目描述。数据保证\(S\)中仅含小写字母。输入文件中不会包含多余的空行,行末不会存在多余的空格。

输出格式:

包含\(n\)行,每行描述一组测试数据的答案,答案的顺序应与输入数据的顺序保持一致。对于每组测试数据,仅需要输出一个整数,表示这组测试数据的答案对\(1,000,000,007\)取模的结果。输出文件中不应包含多余的空行。

输入输出样例

输入样例#1:

3
aaaaa
ab
abcababc
输出样例#1:

36
1
32

说明

测试点编号 约定
1 \(N≤5,L≤50\)
2 \(N≤5,L≤200\)
3 \(N≤5,L≤200\)
4 \(N≤5,L≤10,000\)
5 \(N≤5,L≤10,000\)
6 \(N≤5,L≤100,000\)
7 \(N≤5,L≤200,000\)
8 \(N≤5,L≤500,000\)
9 \(N≤5,L≤1,000,000\)
10 \(N≤5,L≤1,000,000\)

题解:

    既然题面中反复提到KMP,那这道题就应该与KMP紧密相关。

    我们知道,当模式串匹配自己失配时,会立即跳到下一个nxt[]去,在nxt[]为0之前,跳了多少个nxt就说明有多少个与后缀相同的前缀,也是nxt的其中一个定义。这样我们就有了\(O(n^2)\)暴力算法,求完\(nxt[i]\)后,递归nxt,看有多少次值在\(\lfloor \frac i2\rfloor\)以内。

    考虑优化这个递归过程。因为现在的\(nxt[i]\)可以从前面的\(nxt[j]+1\)转移过来,因此现在的\(num[i]\)也可以从前面的\(num[j]+1\)转移过来。于是\(nxt[i]\)只从\(\le \lfloor \frac i2\rfloor\)转移。于是有了下面这段代码:

for(int i=2,j=0;i<=n;++i)
{
while(j&&(s[j+1]!=s[i]||j+1>(i>>1)))//保证了只从i>>1转移过来,j+1是考虑匹配上了会增加1
j=nxt[j];
if(s[j+1]==s[i])
++j;
nxt[i]=j;
num[i]=num[j]+1;
}

    交上去……0分?手测了一下发现会有这种情况:

aaaaaaa

    \(num[7]\)按照上面的代码应该从\(num[3]\)转移得到\(num[7]=2\),但是观察发现\(num[7]\)应该=3。为什么呢?\(num[3]\)严格遵守了前后缀不重叠,但是到了\(num[7]\)就没有了\(num[3]\)的约束,也就是\(num[3]\)不能从\(num[2]\)转移,但是\(num[7]\)可以,这样中间\(num[2]\)就会丢失。

    所以,为了不丢失\(num[2]\)我们试着让\(num[i]\)表示可重叠的相等前后缀的个数,只在统计答案时从前面转移就好了。

    其实就是在做第二遍模式串匹配,此时和第一遍一样,只是要控制\(j\le \lfloor \frac i2\rfloor\),然后更新存储答案的\(num1[i]=num[j]+1\)。

Code:

#include<cstdio>
#include<cstring>
char s[1000005];
long long num[1000001],num1[1000001];
int nxt[1000001];
void work()
{
scanf("%s",s+1);
int n=strlen(s+1);
num[0]=-1;
for(int i=2,j=0;i<=n;++i)
{
while(j&&s[j+1]!=s[i])
j=nxt[j];
if(s[j+1]==s[i])
++j;
nxt[i]=j;
num[i]=num[j]+1;
}
for(int i=2,j=0;i<=n;++i)
{
while(j&&(s[j+1]!=s[i]||j+1>(i>>1)))//和上面的比只加了一个条件
j=nxt[j];
if(s[j+1]==s[i])
++j;
num1[i]=num[j]+1;//从前面的nxt转移过来
}
long long ans=1;
for(int i=1;i<=n;++i)
{
//printf("%d ",num1[i]);调试用
ans*=num1[i]+1;
ans%=1000000007;
}
printf("%lld\n",ans);
return; }
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
work();
return 0;
}

【KMP】洛谷P2375 [NOI2014]动物园 题解的更多相关文章

  1. 【题解】洛谷P2375 [NOI2014] 动物园(KMP)

    洛谷P2375:https://www.luogu.org/problemnew/show/P2375 思路 这道题可以说是完全刷新了本蒟蒻对KMP的理解 感觉对next数组的理解上升到一个新的高度 ...

  2. 洛谷 P2375 [NOI2014]动物园 解题报告

    P2375 [NOI2014]动物园 题目描述 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定 ...

  3. [洛谷P2375] [NOI2014]动物园

    洛谷题目链接:[NOI2014]动物园 题目描述 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决 ...

  4. 洛谷 P2375 [NOI2014]动物园

    题目传送门 解题思路: 其实对于一个sum[i],其值就等于sum[next[i]] + sum[next[next[i]]] + ... + 1,然后我们可以记忆化,然后题目里又有一个限制,就是前后 ...

  5. 洛谷2375 NOI2014动物园(KMP)

    题目链接: 题目. 简单一点来说,这个题就是求一个字符串的\(num\)数组的和,其中有\(num[i]\)表示1~i中有多少个不交叉的相等的前缀和后缀 的数目,要求一个\(O(n)\)的做法 QwQ ...

  6. 2021.11.16 P2375 [NOI2014] 动物园(EXKMP+差分)

    2021.11.16 P2375 [NOI2014] 动物园(EXKMP+差分) https://www.luogu.com.cn/problem/P2375 题意: PS:这道神题的背景让人疑惑,重 ...

  7. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

  8. 【洛谷P3960】列队题解

    [洛谷P3960]列队题解 题目链接 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n×m ...

  9. 洛谷P2312 解方程题解

    洛谷P2312 解方程题解 题目描述 已知多项式方程: \[a_0+a_1x+a_2x^2+\cdots+a_nx^n=0\] 求这个方程在 \([1,m]\) 内的整数解(\(n\) 和 \(m\) ...

随机推荐

  1. c++ 装饰模式(decorate)

    装饰模式:动态地给一个对象添加一些额外的职责.就增加功能来说,装饰模式相比生成子类 更为灵活.有时我们希望给某个对象而不是整个类添加一些功能.比如有一个手机,允许你为手机添加特性,比如增加挂件.屏幕贴 ...

  2. qt数据库有效插件为空的情况

    打了一周的环境,从ubuntu到win7,搭建环境的时间比写代码的时间都多.先简单的介绍一下我搭建的环境不是纯QT环境,是一个芬兰的软件开发商开发出来的SDK里面完全融合qt,其中qt是以单独的目录存 ...

  3. boost 错误报告

    #include <Windows.h> #include <boost/asio.hpp> 编译器会报错,fatal error C1189: #error :  WinSo ...

  4. PCL struct“flann::SearchParams参数错误

    最近在使用PCL的KdTreeFLANN的时候报错:error C2079: “pcl::KdTreeFLANN<PointT>::param_radius_”使用未定义的 struct“ ...

  5. hibernate mapping文件中 xmlns会导致linq to xml 查询不到对应的节点

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. unbutu下wireshark编译安装(已更新)

    今天下午在ubuntu下进行编译安装wireshark,过程中出了很多错误,但最终安装成功了,这里写下自己的安装步骤和方法,有参考博文的安装编译方法,也有自己的总结和心得. 1 安装编译工具 $sud ...

  7. Web大文件上传控件-示例更新-Xproer.HttpUploader6.2

    版权所有 2009-2016荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...

  8. 好的linux网站

    site:www.tldp.org rpm http://www.computerhope.com/unix.htm

  9. mysql 全文搜索 FULLTEXT

    到 3.23.23 时,MySQL 开始支持全文索引和搜索.全文索引在 MySQL 中是一个 FULLTEXT 类型索引.FULLTEXT 索引用于 MyISAM 表,可以在 CREATE TABLE ...

  10. poj1860 Currency Exchange(spfa判断正环)

    Description Several currency exchange points are working in our city. Let us suppose that each point ...