#哈希,数论#洛谷 3538 OKR-A Horrible Poem

题目

求子串的最短循环节长度


分析

首先,有一些很重要的要说

  1. 循环节长度|子串长度
  2. n n n是某段的循环节,那么 k ∗ n k*n kn也是循环节
  3. n n n是某段的循环节,满足$[l…r-x]=[l+x…r]

所以可以通过这样的方法,首先线性筛质数,从大到小枚举约数,求得答案


代码

#include <cstdio>
unsigned int n,m,prime[41541],q,v[500001],p[261],pw[500001],key[500001]; char s[500001];
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
bool check(int l1,int r1,int l2,int r2){
    return key[r1]-pw[r1-l1+1]*key[l1-1]==key[r2]-pw[r2-l2+1]*key[l2-1];
}
int main(){
	n=in(); scanf("%s",s+1); q=in(); pw[0]=1;
	for (register int i=2;i<=n;i++){//线性筛
		if (!v[i]) v[i]=i,prime[++m]=i;
		for (register int j=1;j<=m;j++){
			if (prime[j]>v[i]||prime[j]>n/i) break;
			v[i*prime[j]]=prime[j];
		}
	}
	for (register int i=1;i<=n;i++) pw[i]=pw[i-1]*263,key[i]=key[i-1]*263+s[i]-96;//哈希值
	while (q--){
		int l=in(); int r=in();
		int len=r-l+1,k=0;
		while (len>1){//可能的枚举方案
			p[++k]=v[len];
			len/=v[len];
		}
		len=r-l+1;
		for (register int i=1;i<=k;i++){
			int t=len/p[i];
			if (check(l,r-t,l+t,r)) len=t;//O(1)查询
		}
		printf("%d\n",len);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值