题解 [NOI2014] 动物园
题目大意
对于字符串 num[i]
。例如 aaaaa
,则num[4]=2 num[4] = 2 num[4]=2
。这是因为 aaaa
,其中 a
和 aa
都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而 aaa
虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。
字符串长度
/*
观察:
nxt[i]: 字符串有前i个字符构成的的前缀最长的相等的真前缀和真后缀的长度 (嵌套的!)(真前/后缀:与原串不相等的前/后缀)
num[i]:字符串前i个字符不重叠的相等前后缀数量
注意到对于s[1..i]: s[1..nxt[i]], s[1..nxt[nxt[i]]], s[1..nxt[nxt[nxt[i]]]], ... 是s[1..i]的全部的相等前后缀
所以只需要不断跳nxt[i]直到不重叠即可
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
char a[maxn];
int nxt[maxn]; // KMP的nxt
int v[maxn]; // v[i]是前i个字符的前缀的前缀和后缀相同的个数(嵌套的)
int mul(int x, int y) {
return static_cast<long long>(x) * y % mod;
}
int main() {
int T;
scanf("%d", &T);
for (int _ = 0; _ < T; ++_) {
scanf("%s", a + 1);
int n = strlen(a + 1);
int j = 0;
v[1] = 1; // 初始时,只有1个字符,此时它自己的前缀和后缀都可以是它自己,故为1
for (int i = 2; i <= n; ++i) {
while (j > 0 && a[j + 1] != a[i]) {
j = nxt[j];
}
j += a[j + 1] == a[i];
nxt[i] = j;
v[i] = v[j] + 1; // 递推,1是前缀i自己的贡献(一个字符串本身既是自己的前缀,又是自己的后缀)
}
j = 0;
int r = 1;
for (int i = 2; i <= n; ++i) {
while (j > 0 && a[j + 1] != a[i]) {
j = nxt[j];
}
j += a[j + 1] == a[i];
// 题目要求前缀和后缀没有重叠部分
// 这里j就是最大前缀(后缀)长度
// 前后缀长度都为j,总共为2*j,而没有重叠部分要求i>=j*2
// 当j*2>i时,通过j=nxt[j]缩短长度
while (j * 2 > i) {
j = nxt[j];
}
// 此时j长度合适,那么题目所求的num[i]显然是v[j]
r = mul(r, v[j] + 1); // 注意题目要求+1
}
printf("%d\n", r);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~