给定字符串,求给定l,r区间的最短循环节长度.
显然区间长度循环节长度是区间长度m的因数,但是这样直接写个q根号m的暴力就肯定T掉啦。
想了挺久发现了一个喵喵的做法,不难发现,每个区间的某个字母必须在一个循环节里都出现,若这个字母出现k次,那么循环节的个数必须是k的因数,那么循环节个数就在这个区间gcd(k)范围内.
我们可以先预处理出前缀和数组保存每个区间每个字母出现次数,预处理除字符串hash值,然后枚举到根号gcd就够了.
这样复杂度是q根号gcd(k),十分玄学的复杂度,于是我在洛谷和loj就T成了90分,可能代码写丑了吧,不过在bzoj上AC了。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <queue> 7 #include <map> 8 #define ll long long 9 #define out(a) printf("%d",a) 10 #define writeln printf(" ") 11 const int N=5e5+50; 12 const int MOD=1e9+7; 13 const int base=233; 14 using namespace std; 15 int n,q,l,r,ln; 16 int num,numn,numns,len,ans=0; 17 ll Hash[N],Pow[N]; 18 int cnt[27][N]; 19 char s[N]; 20 int read() 21 { 22 int s=0,t=1; char c; 23 while (c<'0'||c>'9'){if (c=='-') t=-1; c=getchar();} 24 while (c>='0'&&c<='9'){s=s*10+c-'0'; c=getchar();} 25 return s*t; 26 } 27 ll readl() 28 { 29 ll s=0,t=1; char c; 30 while (c<'0'||c>'9'){if (c=='-') t=-1; c=getchar();} 31 while (c>='0'&&c<='9'){s=s*10+c-'0'; c=getchar();} 32 return s*t; 33 } 34 ll get(int l,int r) 35 { 36 if (l<=r) return (Hash[r]-(ll)Hash[l-1]*Pow[r-l+1]%MOD+MOD)%MOD; 37 return 0; 38 } 39 void hashs(char s[]) 40 { 41 for (int i=0;i<n;i++) 42 Hash[i]=(Hash[i-1]*base+s[i])%MOD; 43 } 44 int gcd(int a,int b) 45 { 46 return a==0?b:gcd(b%a,a); 47 } 48 int main() 49 { 50 n=read(); Pow[0]=1; 51 for (int i=1;i<=n;i++) 52 Pow[i]=Pow[i-1]*base%MOD; 53 scanf("%s",s); ln=strlen(s); 54 for (int i=1;i<=26;i++) 55 for (int j=0;j<ln;j++){ 56 cnt[i][j]=cnt[i][j-1]; 57 if (s[j]-'a'+1==i) cnt[i][j]++; 58 } 59 hashs(s); 60 q=read(); 61 for (int i=1;i<=q;i++){ 62 l=read(),r=read(); len=r-l+1; ans=num=len; l--; r--; 63 for (int i=1;i<=26;i++) 64 num=gcd(num,cnt[i][r]-cnt[i][l-1]); 65 for (int i=1;i*i<=num;i++){ 66 if (num%i==0){ 67 numns=len/(num/i); 68 if (get(l+numns,r)==get(l,r-numns)) { 69 ans=numns; break; 70 } 71 numn=len/i; 72 if (get(l+numn,r)==get(l,r-numn)){ 73 ans=numn; 74 } 75 } 76 } 77 out(ans); 78 writeln; 79 } 80 return 0; 81 }