【题目链接】
# 10038. 「一本通 2.1 练习 4」A Horrible Poem
【参考博客】
A Horrible Poem (字符串hash+数论)
【题目描述】
给出一个由小写英文字母组成的字符串 SS,再给出 qq 个询问,要求回答 SS 某个子串的最短循环节。
如果字符串 BB 是字符串 AA 的循环节,那么 AA 可以由 BB 重复若干次得到。
【算法】-首先对于长度为 lenlen 的子串,循环节长度为 xx 的充要条件:[1,len−x][1,len−x]串的哈希值等于 [x+1,len][x+1,len] 串的哈希值。
假设最短循环节长度为len则原串长度显然为len*k。若只考虑k,并且将k的质因数依次分解,每次试除k,则得到的k。k。和len的乘积仍是循环节,利用这个性质。依次用质因数 ii 试除n,若除去后仍是循环节,说明i属于k,将其除去,结果就留下了len。
【代码】:
1 #include<cstdio>
2 #include<bitset>
3 #include<cstring>
4 #include<algorithm>
5 using namespace std;
6 typedef unsigned long long ULL ;
7 const int N = 5e5+10;
8 ULL h[N],p[N],base = 131;
9
10 char str[N];
11 int n,m,len,ans,tmp,L,R;
12
13 int prime[N],Min_p[N],cnt;
14 void Init(){
15 //memset( is_prime , true , sizeof is_prime );
16 int tot = 0 ;
17 for(int i=2;i<N;i++) {
18 if(!Min_p[i]) {
19 //if( i < 10 ) printf("%d\n",i);
20 prime[++tot]=i;
21 Min_p[i]=i;
22 }
23 for(int j=1;j<=tot;j++) {
24 if(prime[j]>Min_p[i]||prime[j]*i>N) break;
25 Min_p[prime[j]*i]=prime[j];
26 }
27 }
28 cnt = tot ;
29 }
30
31 void get_Hash(){
32 p[0] = 1;
33 for(int i=1;i<=len;i++){
34 p[i] = p[i-1] * base ;
35 h[i] = h[i-1] * base + (ULL) str[i];
36 }
37 }
38
39 bool Vaild( int L , int R , int k ){
40 return h[R] - h[L+k-1] * p[len-k] == h[L + (len/k-1)*k - 1] - h[L-1] * p[len-k] ;
41 }
42 int main()
43 {
44 Init();
45 //for(int i=1;i<10;i++) printf("%d\n",prime[i]);
46 scanf("%d%s%d",&n,str+1,&m);
47
48
49 len = strlen( str+1 ) ;
50 get_Hash();
51 /*
52 for(int i=1;i<=len;i++){
53 printf("%llu\n",h[i]);
54 }
55 */
56 while( m-- ){
57 scanf("%d%d",&L,&R);
58 len = tmp = ans = R - L + 1 ;
59 while( tmp != 1 ){
60 int k = Min_p[tmp] ;
61 while( tmp % k == 0 && Vaild(L,R,ans/Min_p[tmp] ) )
62 tmp /= k , ans /= k ;
63 while( tmp % k == 0 )
64 tmp /= k ;
65 }
66 printf("%d\n",ans);
67 }
68 return 0 ;
69 }