【题目链接】
# 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 ",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 ",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 ",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 ",ans); 67 } 68 return 0 ; 69 }