3977. 密码破译

题目


正解

快速判断长度为(x)的串是不是循环节,只需要判断(len-x)的串是否为区间的border。
哈希处理。
注意到如果(x)不是循环节,则它的因数都不会是循环节。
所以可以枚举(x)的质因子,长度除以质因子之后判断是不是,直到不是循环节为止,这样就可以得知最短循环节的这个质因子的指数。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500010
#define ll long long
int n,m;
char s[N];
const int mo1=1000000007;
const int mo2=1000000009;
ll pw1[N],pw2[N],p1[N],p2[N];
void init(){
	pw1[0]=pw2[0]=1;
	for (int i=1;i<=n;++i){
		pw1[i]=pw1[i-1]*26%mo1;
		pw2[i]=pw2[i-1]*26%mo2;
	}
	for (int i=1;i<=n;++i){
		p1[i]=(p1[i-1]*26+s[i]-'a')%mo1;
		p2[i]=(p2[i-1]*26+s[i]-'a')%mo2;
	}
}
bool eql(int x,int y,int len){
	return ((p1[x+len-1]-p1[x-1]*pw1[len])%mo1+mo1)%mo1==((p1[y+len-1]-p1[y-1]*pw1[len])%mo1+mo1)%mo1
		&& ((p2[x+len-1]-p2[x-1]*pw2[len])%mo2+mo2)%mo2==((p2[y+len-1]-p2[y-1]*pw2[len])%mo2+mo2)%mo2;
}
bool judge(int l,int r,int len){
	len=r-l+1-len;
	return eql(l,r-len+1,len);
}
int p[N],np;
bool inp[N];
int mnp[N];
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d%s%d",&n,s+1,&m);
	init();
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i,mnp[i]=i;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			mnp[i*p[j]]=p[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,ans=1;
		while (len>1){
			int q=mnp[len],tmp=r-l+1;
			while (len%q==0 && judge(l,r,tmp/q))
				len/=q,tmp/=q;
			while (len%q==0)
				len/=q,ans*=q;
		}
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/13696613.html