一.題目鏈接:
HYSBZ-2795
二.題目大意:
給一個長度爲 n 的字符串,q 次詢問,每次問 s[l...r] 的最小循環節.
三.分析:
技巧一:字符串 s[l, r] 具有循環節 k 等價於 s[l, r - k] == s[l + k, r].
技巧二:線性篩中預處理出每個數的最小質因子,可 進行質因數分解.
技巧三:字符串的最小循環節可通過對字符串長度的每個質因子通過試除法求解.
之前找最小循環節的時候我都是去枚舉字符串長度的約數,逐一 check,時間複雜度
考慮另一種方式,設 n 爲字符串長度,ans 爲最小循環節長度。
ans 的初始值不妨設爲 n,即字符串自身是一個循環節.
對 n 進行質因子分解得到
那麼去檢查 是否爲循環節
若是,再去檢查 是否爲循環節
否則,再去檢查 是否爲循環節.
四.代碼實現:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int M = (int)5e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)19890907;
const ull base = 131;
char s[M + 5];
ull p[M + 5];
ull f[M + 5];
bool is_prime[M + 5];
int prime[M + 5], cnt;
int v[M + 5];//最小質因子
void init()
{
memset(is_prime, 1, sizeof(is_prime));
is_prime[0] = is_prime[1] = 0;
for(int i = 2; i <= M; ++i)
{
if(is_prime[i])
{
prime[++cnt] = i;
v[i] = i;
}
for(int j = 1; j <= cnt && i * prime[j] <= M; ++j)
{
is_prime[i * prime[j]] = 0;
v[i * prime[j]] = prime[j];
if(i % prime[j] == 0)
{
break;
}
}
}
}
ull cal(int l, int r)
{
return f[r] - f[l - 1] * p[r - l + 1];
}
bool check(int l, int r, int k)
{
return cal(l, r - k) == cal(l + k, r);
}
int main()
{
init();
int n; scanf("%d", &n);
scanf("%s", s + 1);
p[0] = 1; for(int i = 1; i <= n; ++i) p[i] = p[i - 1] * base;
f[0] = 1; for(int i = 1; i <= n; ++i) f[i] = f[i - 1] * base + s[i] - 'a' + 1;
int q; scanf("%d", &q);
while(q--)
{
int l, r; scanf("%d %d", &l, &r);
int len = r - l + 1, ans = r - l + 1;
while(len != 1)
{
int t = v[len];
while(len % t == 0) len /= t;
while(ans % t == 0 && check(l, r, ans / t)) ans /= t;
}
printf("%d\n", ans);
}
return 0;
}