密码破译
Description
给出一个长度为
n
,由小写字母组成的母串。
给出
一个字符串的价值定义为一个最短的
T
(某个字符串),使得
则该字符串的关键值即为
T
的长度。
Data Constraint
Solution
这是一道伪装的水题。
题目让我们求母串的一个子串的最短循环节。
接下来给出两个显然的结论(不给出证明了,请读者自行思考)
1、串
T
有一个长度为
2、若一个长度为
Len
的前缀不是
T
的循环节,那么
有了以上两个结论这题就好做了。
一开始设它的
ans
为子串的长度
Len
,枚举
Len
的所有质因子,假设枚举到当前的质因子为
p
,若
至于判断两个子串是否相同,可以用
质因子可以用线筛预处理好。
由于每个质因子都大于等于
2
,所以Len的质因子个数不会超过
所以总的复杂度为
O
(
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
using namespace std;
typedef long long ll;
const ll N=52e4,k1=23,k2=29,shit=666,F_word=168;
const ll m1=1e9+7,m2=998244353,m3=1004535809;
char s[N];
ll s1[N],s2[N],j1[N],j2[N],n1[N],n2[N];
bool bz[N];
int from[N],ss[N],n,q;
inline ll ksm(ll o,ll t,ll mo)
{
ll y=1;
for(;t;t>>=1,o=o*o%mo)
if(t&1)y=y*o%mo;
return y;
}
inline int read()
{
int o=0; char ch=' ';
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
return o;
}
ll get(int a,int b)
{
ll k1=(s1[b]-s1[a-1]+m1*2)*n1[a]%m1;
ll k2=(s2[b]-s2[a-1]+m2*2)*n2[a]%m2;
return (k1*shit+k2*F_word)%m3;
}
int main()
{
cin>>n;
scanf("%s",s+1);
s1[1]=s2[1]=s[1]-97; j1[1]=j2[1]=1;
fo(i,2,n)
{
j1[i]=j1[i-1]*k1%m1;
j2[i]=j2[i-1]*k2%m2;
s1[i]=(s1[i-1]+j1[i]*(s[i]-97))%m1;
s2[i]=(s2[i-1]+j2[i]*(s[i]-97))%m2;
}
n1[n]=ksm(j1[n],m1-2,m1);
n2[n]=ksm(j2[n],m2-2,m2);
fd(i,n-1,1)n1[i]=n1[i+1]*k1%m1,n2[i]=n2[i+1]*k2%m2;
n1[0]=n2[0]=1;
int o=0;
fo(i,2,n)
{
if(!bz[i])ss[++o]=i,from[i]=i;
fo(j,1,o){
if(i*ss[j]>n)break;
from[i*ss[j]]=ss[j];
bz[i*ss[j]]=true;
if(i%ss[j]==0)break;
}
}
cin>>q;
fo(i,1,q){
int le=read(),ri=read();
int len=ri-le+1;
int k=len,ans=k;
for(;k!=1;k/=from[k])
if(ans%from[k]==0)
if(get(le,ri-ans/from[k])==get(le+ans/from[k],ri))ans/=from[k];
printf("%d\n",ans);
}
}