P3538 [POI2012]OKR-A Horrible Poem 解题报告
Description
给定一个字符串 , 次询问,每次循环区间 的最短循环节长度。
Solution
一开始看到最短循环节想到 KMP,但是KMP不能处理子区间,转而考虑 hash。
这道题最妙的在于利用循环节性质优化时间。
- 循环节长度和循环次数一定是该字符串的因数
- 一个循环节长度一定是比它长的循环节的因数
于是我们可以用 euler筛 把枚举质因数优化到 。
就做完了。
Code

#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} return x * f; } const int N = 5e5 + 10, base = 233, mod = 1e9 + 7; int n, q; char s[N]; int hs[N], pw[N]; int tot, g[N], pri[N]; bool vis[N]; inline void euler() { for (int i = 2; i <= n; ++ i) { if (!vis[i]) pri[++tot] = i, g[i] = i; for (int j = 1; j <= tot && i * pri[j] <= n; ++ j) { vis[pri[j] * i] = 1; g[pri[j] * i] = pri[j]; if (i % pri[j] == 0) break; } } } inline int calc(int l, int r) { return ((hs[r] - hs[l-1] * pw[r-l+1]) % mod + mod) % mod; } signed main () { n = read(); euler(); scanf("%s", s + 1); pw[0] = 1; for (int i = 1; i <= n; ++ i) hs[i] = (hs[i-1] * base + s[i] - 'a' + 1) % mod, pw[i] = (pw[i-1] * base) % mod; q = read(); while (q --) { int l = read(), r = read(), ans, len; ans = len = r - l + 1; if (calc(l+1, r) == calc(l, r-1)) {puts("1"); continue;} while (len > 1) { if (calc(l + ans / g[len], r) == calc(l, r - ans / g[len])) ans /= g[len]; len /= g[len]; } printf("%lld\n", ans); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效