题目
Description
某年某月某日, r 64 打开了某个无聊的密码网站。
作为一个热衷于破译密码的SB, r 64 的IQ是非常高的,但是,一个人是无法在
一瞬间完成无数份密码的破译工作的(也许两瞬间),所以这个任务就交给你和
他共同来完成。
一个密码对应一个 字符串 (仅包含 小写英文字母 ),令这个串为S。你成功地
破解了这个密码串,当且仅当你找到了一个 最短 的T,使得S = T K ,即T重复K次。
密码S的关键值即为T的 长度 。
这个网站的密码有一个特性,它是一个很长很长的 母密码串的一段 ,碰巧的
是,你无意当中窃取到了这个密码串。作为一台十分负责的计算机,请帮助 r 64 解
决这个问题。
Input
输入文件 password.in 共m + 3行。第一行为正整数n,第二行为一个长为n的字
符串s。第三行为一个正整数m,表示密码串的个数。第四至m + 3行每行两个正整
数L,R(L ≤ R),表示询问的密码串在母密码串中的位置。
Output
输出文件 password.out 共m行。每一行一个正整数,表示对应密码串的关键值。
Sample Input
6
aababa
3
1 3
1 2
3 6
Sample Output
3
1
2
Data Constraint
测试点编号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 数据范围
n ≤ 100, m ≤ 100
n ≤ 100, m ≤ 100
n ≤ 1000, m ≤ 1000
n ≤ 4000, m ≤ 4000
n ≤ 5000, m ≤ 5000
n ≤ 5000, m ≤ 5000
n ≤ 40000, m ≤ 90000
n ≤ 70000, m ≤ 100000
n ≤ 100000, m ≤ 100000
n ≤ 100000, m ≤ 100000
n ≤ 300000, m ≤ 600000
n ≤ 300000, m ≤ 600000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
n ≤ 500000, m ≤ 2000000
思路
考虑如何判断当前串的最短循环节,一个O(N)的做法是枚举长度,然后判断它是否为循环节。那么如何高效地判断循环节呢?还记得学过的KMP算法吗?串T有一个长为Len循环节的充要条件为T的前N – Len位与后N – Len位是一样的,而且满足Len | N。那么可以通过Hash直接比较。
接下来考虑优化暴力。假设一个长度为Len的前缀不是T的循环节,那么Len的所有约数肯定也不是,证明显然。所以我们可以确定每一个质因子出现了多少次,然后判断ans / 这个质因子是否为一个循环节,若是,则 ans /= 质因子。由于枚举的是每一个数的质因子,所以单次询问的复杂度为O(logN)。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
const int N=6e5+77,mod=1e9+7;
int p[N],q[N],sum[N];
bool bo[N];
char ch[N];
inline ll power(ll x,ll t)
{
ll b=1;
while(t)
{
if(t&1) b=b*x%mod; x=x*x%mod; t>>=1;
}
return b;
}
inline int getsum(int l,int r)
{
return (sum[r]-1ll*sum[l-1]*power(256,r-l+1)%mod+mod)%mod;
}
int main() {
int n,m,num;
scanf("%d\n%s\n%d",&n,ch+1,&m);
sum[0]=0;
for(int i=1; i<=n; ++i)
sum[i]=(1ll*sum[i-1]*256+ch[i])%mod;
num=0;
for(int i=2; i<=n; ++i) {
if(!bo[i]) {
p[++num]=i;
q[i]=num;
}
for(int j=1; j<=num&&p[j]*i<=n; ++j) {
bo[i*p[j]]=1;
q[i*p[j]]=j;
if(i%p[j] == 0)
break;
}
}
for(int i=1; i<=m; ++i) {
int l,r;
scanf("%d %d",&l,&r);
int len=r-l+1;
for(int j=len; j > 1; j /= p[q[j]])
{
int sum1=getsum(l+len/p[q[j]],r);
int sum2=getsum(l,r-len/p[q[j]]);
if(sum1 != sum2) continue;
len /= p[q[j]];
}
printf("%d\n",len);
}
}