@bzoj - [email protected] [Noi2014]动物园

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

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

某天,园长给动物们讲解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取模的结果即可。

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

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

Sample Input
3
aaaaa
ab
abcababc
Sample Output
36
1
32

HINT
n≤5,L≤1,000,000

@[email protected]

题面暗示题解.jpg。

可以以 border 的角度理解这个题,也可以以 border 的角度更好地理解 kmp 算法。
什么是 border?即字符串所有相同的前缀与后缀构成的集合,或者说 S[1...i] = S[lenS-i+1...lenS] 对应的 S[1...i]。
那么 kmp 所求的实际上就是前 i 个字符对应的最长 border 长度 next[i]。

注意到 next[i], next[next[i]], ... 实际上包含了前 i 个字符的所有 border 长度。
那么这道题就可以用倍增求出第一个长度 <= i/2 的 border 长度 fir[k]。
我们可以在 kmp 的时候递推出前缀 k 又有多少 border,记作 cnt[k]。对于 i 来说,num[i] = cnt[k] + 1(多一个 1 是因为 k 本身也是合法的)。

但是倍增是 log 的,而这道题显然卡 log 做法。
我们可以考虑类比 kmp 的求解方法,在求前缀 i 的时候充分利用前缀 i-1 的信息。
具体到这一道题,有 fir[i] <= fir[i-1] + 1
那么从 fir[i-1] 开始暴力跳 next 往回找第一个可以拼接出前缀 i 的 border 的位置,这个位置 + 1 就是我们要找的 fir[i]。
时间复杂度和 kmp 的证明方法一样。

@accepted [email protected]

#include <cstdio>
#include <cstring>
const int MAXN = 1000000;
const int MOD = int(1E9) + 7;
int f1[MAXN + 5], f2[MAXN + 5];
int num[MAXN + 5], cnt[MAXN + 5];
char S[MAXN + 5];
void solve() {
    scanf("%s", S);
    int lenS = strlen(S);
    f1[0] = cnt[0] = -1, f1[1] = 0;
    for(int i=2;i<=lenS;i++) {
        int j = f1[i - 1];
        while( j != -1 && S[j] != S[i-1] )
            j = f1[j];
        f1[i] = j + 1, cnt[i] = cnt[f1[i]] + 1;
    }
    f2[0] = -1, f2[1] = 0;
    for(int i=2;i<=lenS;i++) {
        int j = f2[i - 1];
        while( j != -1 && (S[j] != S[i-1] || 2*(j + 1) > i) )
            j = f1[j];
        f2[i] = j + 1, num[i] = cnt[f2[i]] + 1;
    }
    int ans = 1;
    for(int i=1;i<=lenS;i++)
        ans = 1LL*ans*(num[i] + 1)%MOD;
    printf("%d\n", ans);
}
int main() {
    int n; scanf("%d", &n);
    for(int i=1;i<=n;i++)
        solve();
}

@[email protected]

P.S:本篇博客是供博主复习所用,所以有很多简略的地方以及描述不清的地方。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11845374.html

时间: 2024-10-10 12:54:56

@bzoj - [email protected] [Noi2014]动物园的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu

@bzoj - [email&#160;protected] 旅行规划

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 请你维护一个序列,支持两种操作: (1)某个区间 [x, y] 内的数同时加上一个增量 k. (2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和. input 第一行给出一个整数 n.n <= 100000.接下来一行 n 个整数表示序列的初始值. 第三行给出一个整数 m,

@bzoj - [email&#160;protected] Hard Nim

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 堆石子,每堆石子的数量是不超过 m 的一个质数. 两个人玩 nim 游戏,问使后手必胜的初始局面有多少种. 模 10^9 + 7. input 多组数据.数据组数 <= 80. 每组数据一行两个正整数,n 和 m.1 <= n <= 10^9, 2 <= m <

@bzoj - [email&#160;protected] [Cqoi2016]伪光滑数

目录 @description@ @[email protected] @version - [email protected] @version - [email protected] @accepted [email protected] @[email protected] @description@ 若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 \(A_k\),并且满足 \(A_k^k \le N\),\(A_k < 128\),我们就称整数 M 为 N - 伪光

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个