P3538 [POI2012]OKR-A Horrible Poem 解题报告

Description

给定一个字符串 sq 次询问,每次循环区间 [l:r] 的最短循环节长度。

Solution

一开始看到最短循环节想到 KMP,但是KMP不能处理子区间,转而考虑 hash。

这道题最妙的在于利用循环节性质优化时间。

- 循环节长度和循环次数一定是该字符串的因数
- 一个循环节长度一定是比它长的循环节的因数

于是我们可以用 euler筛 把枚举质因数优化到 logn

就做完了。

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;
}
View Code
复制代码

 

posted @   LikC1606  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示