「一本通 2.1 练习 4」A Horrible Poem
[POI]2012 OKR-A Horrible Poem
思路
看许多题解都是质因数分解,我来一个不一样的
-
首先暴力分是很好得滴,直接暴力枚举合法的答案即可,但是他跑死了---40分
-
换个方向,如果 \(S\) 是 \(l-r\) 的循环节的话,那么我们有 \(hs[l,r-s] = hs[s,r]\) ,\(O(1)\)得到答案----60分!!!
-
可他还是会 \(T\) ,我们再将其进行优化:
有数学和规律发现:字串 \(len\) 和每个字母个数的总公约数为循环节的个数
显然公约数越大,循环节的数量也就越多,对应的答案区间也就越小
得到公约数后,从头枚举公约数就好了,可是他还是会 \(T\) ---------------------80分!!正解 从头到尾可能很长,我们不妨用两头跑的方法,枚举公约数的约数,在得到对应的约数的商,这样就可以两头枚举了,这样就AC了 --------------------100分!!!!
注意
- 如果还是 \(T\) 那就是您做了无用的冗杂处理,这里列出作者的错误
- 进制太大( \(131\) 或 \(3\) 即可),最后被卡---\(1.01s\) !!!!
- 无用的 \(strlen\) !!!!
代码
/*
直接暴力枚举可能性即可,但是他跑死了。
27 ——> 47(洛谷60) —— >97(MLE)--->97(TLE,1.01S)---->100
区间长度len和每个字母个数的总公约数为循环节的个数
显然公约数越大,循环节的数量也就越多,对应的答案区间也就越小
得到公约数后,就可以从大到小枚举区间,然后检查区间是否符合条件,符合条件的就break;
这里约数和区间的小技巧
从小枚举最大公约数的约数,换句话说就是将区间从小的开始枚举,在比较的过程中,可以也记录下
枚举的约数的区间是否满足条件(防止循环节就一个);
MLE:进制循环大于开的数组空间
TLE:1.进制太大,进制为3,速度955ms,
2.卡时间(1.01s),删除(strlen),速度985;
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#define F(i,l,r) for(int i=l;i<=r;i++)
#define FK(x) memset(x,0,sizeof(0))
#define int unsigned long long
using namespace std;
/*-----------------------------*/
const int manx = 5e5 + 10;
//const int mod = 1e9+9;
const int base = 3;
inline int read(){
char c = getchar();int x = 0,f = 1;
for(;!isdigit(c);c = getchar())if(c == '-') f = -1;
for(;isdigit(c);c = getchar()) x = x*10 + (c^48);
return x * f;
}
int n, p[manx], m, sum[manx], num[500010][26], x, y;
char a[manx];
inline void prepare(){
p[0] = 1;
F(i,1,n) p[i] = p[i-1]*base;
}
inline int apart(int l, int r){
return (sum[r] - sum[l-1]*p[r-l+1] ) ;
}
inline bool check(int l, int lenth){
int x1 = apart(x,y-l),x2 = apart(x+l,y);
if(x1 == x2) return 1;
return 0;
}
inline int gcd(int x,int y){
return y ? gcd(y, x%y) : x;
}
signed main(){
// freopen("d.in","r",stdin);
// freopen("d.out","w",stdout);
n = read();
prepare();
scanf("%s", a + 1);
int s = strlen(a+1);
m = read();
F(i,1,s) sum[i] = (sum[i-1]*base + a[i]) ;
F(i,1,s)
F(j,1,26){
num[i][j] = num[i-1][j] + (a[i]-'a'+1==j);
}
while(m--){
x = read(), y = read();
int ans = 0, l = 1, gcd_;
int k = y - x + 1;
gcd_ = k;
F(i,1,26) gcd_= gcd(gcd_, num[y][i] - num[x-1][i]);
for(int i = 1; i * i<= gcd_; i++){
if(gcd_% i == 0){
if(check(k/(gcd_/i), k)){ans = max(ans, gcd_/i);break;}
if(check(k/i, k)) ans = max(ans,i);
}
}
printf("%d\n",k/ans);
}
return 0;
}