题面: 洛谷
题解:
首先我们需要知道一个性质,串s的最小循环节 = len - next[len].其中next[len]表示串s的一个最长长度使得s[1] ~ s[next[len]] == s[len - next[len] + 1] ~ s[len](详细定义参见KMP)
至于为什么是成立的可以画图推一下,这个应该是比较常见的性质。
可能画的有点丑。。。
图中每个绿色方块所代表的串都是相同的,因为第一个绿块显然与下面那块相同,而根据next的定义,它也与第二行第二个绿块相同……以此类推,可以一直递推下去,直到推完整个数组。
对于一个长度为len的串s而言,设它的最小循环节长度为l.
若$len = p_{1}^{k_{1}} \cdot p_{2}^{k_{2}} \cdot p_{3}^{k_{3}}...p_{t}^{k_{t}}$
则$len = p_{1}^{a_{1}} \cdot p_{2}^{a_{2}} \cdot p_{3}^{a_{3}}...p_{t}^{a_{t}}$其中$a_{i} \le k_{i}$.
首先一个串的最大循环节长度肯定= len;
而这个循环节之所以可以变小,是因为这个最大循环节是由很多个最短循环节组成。我们假设最短循环节为X,这这个串可以表示为XXXXXXX(若干个X)
假设X有b个。那么我们可以每次对b缩减一个b的因子,最后使得b变为1,即使b不断除一个数。
例如一个串s一开始可以被表示为XXXXXXXX(8个X),即b = 8
这个时候我们枚举到一个2,于是我们判断原串是否可以被XXXX + XXXX凑出,如果可以,那么b /= 2.
然后我们判断原串是否可以被XX + XX + XX + XX凑出,如果可以,那么b /= 2.
依次类推,直到已经没有更小的循环节可以凑出原串位置。
其中2是b的某个质因子。
因为b的最大值为len(即循环节为1),所以我们一开始先从len开始,不断枚举len的质因子,看能否消去这个因子,最后剩下的数就是答案。
判断一个长度是否可以成立,可以用hash判断图中红色部分是否相等
其中绿块长度为x。
原理就是一开始解释过的KMP求最小循环节。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 501000 5 #define p1 1000000007 6 #define p2 998244353 7 #define base 26 8 #define LL long long 9 10 int n, m, tot, top; 11 int hash1[AC], hash2[AC], pw1[AC], pw2[AC]; 12 int pri[AC], last[AC], q[AC]; 13 char s[AC]; 14 bool z[AC]; 15 16 inline int read() 17 { 18 int x = 0;char c = getchar(); 19 while(c > '9' || c < '0') c = getchar(); 20 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 21 return x; 22 } 23 24 void get()//欧拉筛 25 { 26 for(R i = 2; i <= n; i ++) 27 { 28 if(!z[i]) pri[++ tot] = i, last[i] = i; 29 for(R j = 1; j <= tot; j ++) 30 { 31 int now = pri[j]; 32 if(now * i > n) break; 33 z[now * i] = true, last[now * i] = now; 34 if(!(i % now)) break; 35 } 36 } 37 } 38 39 void pre() 40 { 41 n = read(); 42 scanf("%s", s + 1); 43 } 44 45 void build()//求前缀hash值 46 { 47 pw1[0] = pw2[0] = 1; 48 for(R i = 1; i <= n; i ++) 49 { 50 hash1[i] = (1ll * hash1[i - 1] * base + s[i] - 'a' + 1) % p1; 51 hash2[i] = (1ll * hash2[i - 1] * base + s[i] - 'a' + 1) % p2; 52 pw1[i] = 1ll * pw1[i - 1] * base % p1;//存下base的i次方 53 pw2[i] = 1ll * pw2[i - 1] * base % p2; 54 } 55 } 56 57 LL cal(int l, int r, bool w){ 58 if(!w) return ((1ll * hash1[r] - 1ll * hash1[l - 1] * pw1[r - l + 1] % p1) + p1) % p1; 59 else return ((hash2[r] - 1ll * hash2[l - 1] * pw2[r - l + 1] % p2) + p2) % p2; 60 } 61 62 bool check(int l, int r, int x){//测试区间[l, r]的循环结是否可能为x 63 return (cal(l + x, r, 0) == cal(l, r - x, 0)) && (cal(l + x, r, 1) == cal(l, r - x, 1)); 64 } 65 66 void work() 67 { 68 m = read(); 69 for(R i = 1; i <= m; i ++) 70 { 71 int l = read(), r = read(), x = r - l + 1; 72 top = 0; 73 while(x != 1) q[++ top] = last[x], x /= last[x]; 74 x = r - l + 1; 75 for(R i = 1; i <= top; i ++) 76 if(check(l, r, x / q[i])) x /= q[i]; 77 printf("%d\n", x); 78 } 79 } 80 81 int main() 82 { 83 // freopen("in.in", "r", stdin); 84 pre(); 85 get();//处理last数组 86 build();//构建hash数组 87 work(); 88 // fclose(stdin); 89 return 0; 90 }