BZOJ 2795: [Poi2012]A Horrible Poem (Hash+思维)

给定字符串,求给定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("\n")
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 }
View Code

 

posted @ 2018-08-30 22:17  Kaleidoscope233  阅读(189)  评论(0编辑  收藏  举报