A Horrible Poem
作者:互联网
A Horrible Poem
题目:
给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节
n≤500 000 表示S的长度。
q≤2 000 000 表示询问次数。
code:
/*
Time: 1.10
Worker: Blank_space
Source: #10038. 「一本通 2.1 练习 4」A Horrible Poem
*/
/*---------------------------------------------------*/
#include<cstdio>
using namespace std;
/*---------------------------------------------------*/
const int base = 60;
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*---------------------------------------------------*/
long long n, q, h[B << 3], pow[B << 3], prim[B << 3], cnt, c[B << 3], ans;
bool p, vis[B << 3];
char s[B << 3];
/*---------------------------------------------------*/
int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void pair()
{
for(int i = 2; i <= n; i++) if(!vis[i])
for(int j = 1; j * i <= n; j++)
if(!vis[i * j]) vis[i * j] = 1, prim[i * j] = i;
}
bool check(int l, int r, int x)
{
if((h[r - x] - h[l - 1] * pow[r - l - x + 1] % mod + mod) % mod != (h[r] - h[l + x - 1] * pow[r - l - x + 1] % mod + mod) % mod) return 0;
return 1;
}
/*---------------------------------------------------*/
int main()
{
// freopen("okr2.in", "r", stdin);
// freopen("", "w", stdout);
n = read(); scanf("%s", s + 1); q = read(); pow[0] = 1; pair();
for(int i = 1; i <= n; i++) pow[i] = pow[i - 1] * base % mod, h[i] = (h[i - 1] * base % mod + s[i] - 96) % mod;
for(int i = 1; i <= q; i++)
{
int l = read(), r = read();
int len = ans = r - l + 1; p = 0;
while(len > 1)
{
int j = ans / prim[len]; len /= prim[len];
if(check(l, r, j)) ans = j;
}
printf("%lld\n", ans);
}
// fclose(stdin);
// fclose(stdout);
}
思路:
首先 第一个想法是暴力枚举
对于每一个区间 枚举一个子串看是否为循环节 判断的时候我们可以采用哈希 循环枚举每一个长度的子串
很明显 时间复杂度起飞了
考虑优化
能够构成区间循环节的子串的长度一定能被整个区间的长度整除 可以省去不必要的判断
判断的过程不需要循环比较整个串 如果一个子串是循环节 很显然的 其必然是从区间左端开始 且满足 \(s_{(l, r - x)} == s_{(l + x, r)}\)
这样我们将每一次的判断优化为了 \(O(1)\)
确实多过了几个点 到现在已经有六十分了(其实我自己写的代码参考题解之前也只有六十分)
最后一个强力的优化
线筛:可能被选出的循环节的长度一定是长度的质因数
正确性:
循环节的长度若不是区间长度的质因数
若其是质因数的倍数 则一定能由选出的质因数构成
否则其必然无法被整个区间的长度整除
所以我们可以直接预处理出长度的质因数
直接枚举质因数 判断即可
标签:子串,Poem,const,int,循环,Horrible,长度,质因数 来源: https://www.cnblogs.com/blank-space-/p/14258214.html