题目大意:给定一个字符串,多次询问某一子串的循环节
首先循环次数一定是子串长度的约数
因此我们可以枚举子串长度的约数进行验证
验证时选择Hash,验证[x,y-len]和[x+len,y]这两段是否相等,O(1)即可出解
但是这样做总复杂度是O(q√n)的,会T
考虑一个优化,设某个字母在子串中出现了k次,那么循环次数一定是k的约数
因此我们取每个字母在子串中出现次数的Gcd,枚举这个值的约数即可
时间复杂度是O(26q+q√n) 真是不优雅- -
此外说分解质因数是O(nlogn)那位什么心态0- -
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 500500 #define BASE 157 using namespace std; typedef unsigned long long ll; int n,m,ans,cnt[26][M]; char s[M]; ll sum[M],power[M]; void Check(int x,int y,int times) { int len=(y-x+1)-(y-x+1)/times; ll hash1=sum[x+len-1]-sum[x-1]*power[len]; ll hash2=sum[y]-sum[y-len]*power[len]; if(hash1==hash2) ans=max(ans,times); } void Query(int x,int y) { int i,gcd=(y-x+1); ans=0; for(i=0;i<26;i++) gcd=__gcd(gcd,cnt[i][y]-cnt[i][x-1]); for(i=1;i*i<=gcd;i++) if(gcd%i==0) Check(x,y,i),Check(x,y,gcd/i); } int main() { int i,j,x,y; cin>>n; scanf("%s",s+1); for(i=1;i<=n;i++) sum[i]=sum[i-1]*BASE+s[i]; for(power[0]=1,i=1;i<=n;i++) power[i]=power[i-1]*BASE; for(j=0;j<26;j++) for(i=1;i<=n;i++) cnt[j][i]=cnt[j][i-1]+(s[i]-'a'==j); cin>>m; for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); Query(x,y); printf("%d\n",(y-x+1)/ans); } return 0; }