题目描述
近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。
某天,园长给动物们讲解KMPKMP算法。
园长:“对于一个字符串 SS ,它的长度为 。我们可以在 O(L)O(L)的时间内,求出一个名为nextnext的数组。有谁预习了nextnext数组的含义吗?”
熊猫:“对于字符串 SS的前 ii 个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作 。”
园长:“非常好!那你能举个例子吗?”
熊猫:“例 SS 为,则 next[5]=2next[5]=2 。因为 SS 的前 个字符为abcababcab,abab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出next[1]=next[2]=next[3]=0,next[4]=next[6]=1,next[7]=2,next[8]=3next[1]=next[2]=next[3]=0,next[4]=next[6]=1,next[7]=2,next[8]=3 。”
园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在 O(L)O(L)的时间内求出nextnext数组。
下课前,园长提出了一个问题:“KMPKMP算法只能求出nextnext数组。我现在希望求出一个更强大numnum数组一一对于字符串 SS 的前 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作 num[i]num[i] 。例如 SS 为,则 num[4]=2num[4]=2 。这是因为 SS 的前 4 个字符为,其中aa和都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaaaaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,num[1]=0,num[2]=num[3]=1,num[5]=2num[1]=0,num[2]=num[3]=1,num[5]=2 。”
最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出 numnum数组呢?
特别地,为了避免大量的输出,你不需要输出 num[i]num[i] 分别是多少,你只需要输出所有(num[i]+1)(num[i]+1)的乘积,对 1,000,000,0071,000,000,007 取模的结果即可。
输入输出格式
输入格式:
第 1 行仅包含一个正整数 nn ,表示测试数据的组数。
随后 行,每行描述一组测试数据。每组测试数据仅含有一个字符串 SS , 的定义详见题目描述。数据保证 SS 中仅含小写字母。输入文件中不会包含多余的空行,行末不会存在多余的空格。
输出格式:
包含 行,每行描述一组测试数据的答案,答案的顺序应与输入数据的顺序保持一致。对于每组测试数据,仅需要输出一个整数,表示这组测试数据的答案对 1,000,000,0071,000,000,007取模的结果。输出文件中不应包含多余的空行。
输入输出样例
输入样例#1:
3
aaaaa
ab
abcababc
输出样例#1:
36
1
32
解题分析
首先我们发现在求出nextnext数组时可以顺带求出弱化版的numnum数组: 长度可超过i2i2的总数。现在考虑如何去掉这些不合法的情况。
显然对于每个num[i]num[i]我们不能暴力向前跳, 因为如果数据全是aa的话, 将会等于i−1i−1, 一下子被卡到了O(N2)O(N2)。 所以我们可以参照nextnext数组的确定方法, 每次从上一个找到的位置向后跳, 即可大大优化时间复杂度至O(N)。
感觉NOI2014是真的水...蒟蒻博主似乎能A3道题
代码如下:
#include <cstdio>
#include <cstring>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 1000005
#define ll long long
#define MOD 1000000007
int tim, ans[MX], len, nex[MX], now, pre;
char buf[MX];
int main(void)
{
scanf("%d", &tim);
W(tim--)
{
scanf("%s", buf);
memset(nex, 0, sizeof(nex));
len = std::strlen(buf);
ans[0] = 0; ans[1] = 1; now = 1, pre = 0;
W (now < len)//筛出nex数组
{
W (pre && buf[now] != buf[pre]) pre = nex[pre];
pre += (buf[now] == buf[pre]), nex[now + 1] = pre, ans[now + 1] = ans[pre] + 1, ++now;
}
ll ret = 1;
now = 1, pre = 0;
int bound;
W (now < len)
{//pre沿着之前的pre继续跳, 因为肯定前面的pre的位置不会超过now/2
bound = now + 1;
W (pre && buf[now] != buf[pre]) pre = nex[pre];
pre += (buf[now] == buf[pre]);
W ((pre << 1) > bound) pre = nex[pre];//跳多了, 往前跳
ret = ret * (ans[pre] + 1) % MOD;
++now;
}
printf("%lld\n", ret);
}
}