奥赛一本通 1460 A Horrible Poem

1460 A Horrible Poem

题目大意

给定一个字符串,多次询问某个子串的最短循环节。

知识要点

素数筛法、字符串哈希

解题思路

显然循环节的长度必然是这个子串长度的约数,先利用筛法求出每个数的最小质因数,然后每次尝试在总长度中除以最小质因数,要检查某个长度是否为循环周期可以使用字符串哈希。

参考代码

#include <bits/stdc++.h>
using namespace std;

const int N = 500005;
int prime[N], fact[N]; //最小质因子
int screen(int n) { //欧拉筛
    int cnt = 0;
    for(int i = 2; i <= n; i++) {
        if(!fact[i]) prime[cnt++] = fact[i] = i;
        for(int j = 0; j < cnt && i * prime[j] <= n; j++) {
            fact[i * prime[j]] = prime[j];
            if(i % prime[j] == 0) break;
        }
    }
    return cnt;
}

char s[N];
int ha[N], pw[N]; //hash值使用自然溢出
int Hash(int l, int r) {
    return ha[r] - ha[l - 1] * pw[r - l + 1];
}
int main() {
    int n, q, l, r;
    scanf("%d%s%d", &n, s + 1, &q);
    screen(n);
    pw[0] = 1;
    for(int i = 1; i <= n; i++)
        ha[i] = ha[i - 1] * 233 + s[i], pw[i] = pw[i - 1] * 233;

    while(q--) {
        int l, r, m, len, ans;
        scanf("%d%d", &l, &r);

        m = ans = r - l + 1;
        while(m > 1) { //分解质因数
            len = ans / fact[m], m /= fact[m]; //尝试缩短循环周期
            if(Hash(l + len, r) == Hash(l, r - len)) ans = len; 
        }
        printf("%d\n", ans);
    }
    return 0;
} 
posted @ 2026-09-22 10:21  萧西待水  阅读(14)  评论(0)    收藏  举报