CF1043G Speckled Band
CF1043G Speckled Band
\(Description\)
给定字符串 \(S\),每次询问 \((l, r)\),将子串 \([l, r]\) 划分为若干段使得至少有两段相同,求拆分后 本质不同的段 的最小个数。
\(Solution\)
结果为\(-1\) 当且仅当子串中每个字符只出现一次,否则可以发现 \(ans = 1/2/3/4\)。
令 \(len = r - l + 1\),以下 \(A, B\) 表示字符串。
- \(ans = 1\),此时子串为 \(AAAA...\),枚举 \(len\) 的约数 \(c\),\([l. r]\) 有长度为 \(c\) 的循环节的充要条件为 \([l, r - c] = [l + c, r]\)。
- \(ans = 2\),子串为 \(ABA \ / \ AAB \ / \ BAA\),前者可以求出子串的最短 \(border\) 判断;对于后两者,考虑求出 \(f_i, \ g_i\) 表示以 \(i\) 为 开头/结尾 的最短 \(AA\) 串长度,那么 \(f_l \leq len \ or \ g_r \leq len\)。
- \(ans = 3\),子串为 \(ABAC \ / \ BACA \ / \ BAAC\),对于前二者,只需要判断 \([l, r]\) 中是否有字符出现超过一次(以它们为 \(A\));后者只需要判断 \(\min_l^r l + f_l \leq r\)。
- 否则 \(ans = 4\)。
求最短 \(border\):
- 首先枚举长度为 \(1 \text{~} \sqrt n\) 的 \(border\),否则最短 \(border\) 与 \([l, r]\) 的后缀排名不超过 \(\sqrt n\)。
- 考虑反证法,若 \(|border| > \sqrt n\) 且与 \([l, r]\) 的后缀排名超过 \(\sqrt n\),则可以证明有更短的 \(border\),与要求矛盾。
求 \(f_i\) :
- 方法与 NOI2016 优秀的拆分 类似。
- 考虑求长度为 \(2L\) 的 \(AA\) 串,将原串每 \(L\) 位设置关键点,则长为 \(2L\) 的 \(AA\) 串一定经过且只经过两个关键点。
- 可以以关键点将 \(A\) 分为 \(LCP\) 和 \(LCS\)。
- 求出相邻关键点的 \(lcp\) 和 \(lcs\) ,那么形成 \(AA\) 串的充要条件为 \(lcp + lcs > L\),开头区间为 \([l - lcp + 1, l - len + lcs]\)。
- 求 \(g\) 类似。
\(Code\)
#include <bits/stdc++.h>
using namespace std;
#define N 200000
#define L 18
#define fo(i, x, y) for(int i = x, end_##i = y; i <= end_##i; i ++)
#define fd(i, x, y) for(int i = x, end_##i = y; i >= end_##i; i --)
#define mcp(a, b) memcpy(a, b, sizeof b)
#define Mes(a, x) memset(a, x, sizeof a)
void read(int &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}
char ch[N + 1];
int n;
struct HASH {
int h[N + 1], mi[N + 1], inv[N + 1], p, mo;
int pw(int x, int k) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * x % mo;
x = 1ll * x * x % mo;
k >>= 1;
}
return res;
}
void build(int x, int y) {
mo = x, p = y;
mi[0] = 1;
fo(i, 1, n) mi[i] = 1ll * mi[i - 1] * p % mo;
inv[n] = pw(mi[n], mo - 2);
fd(i, n, 1)
inv[i - 1] = 1ll * inv[i] * p % mo;
fo(i, 1, n)
h[i] = (h[i - 1] + 1ll * (ch[i] - 'a' + 1) * mi[i] % mo) % mo;
}
int hash(int l, int r) {
return 1ll * (h[r] - h[l - 1] + mo) % mo * inv[l] % mo;
}
} h1, h2;
bool check(int l, int r, int x, int y) {
return h1.hash(l, r) == h1.hash(x, y) && h2.hash(l, r) == h2.hash(x, y);
}
int Log[N + 1];
struct SA {
int sa[N + 1], rk[N + 1], ht[N + 1], id[N + 1], oldrk[N << 1], buc[N + 1], px[N + 1], ft[N + 1][L + 1];
int sk;
void mysort() {
fill(buc, buc + 1 + sk, 0);
fo(i, 1, n) ++ buc[ px[i] = rk[id[i]] ];
fo(i, 1, sk) buc[i] += buc[i - 1];
fd(i, n, 1) sa[ buc[px[i]] -- ] = id[i];
}
bool pd(int x, int y, int z) { return oldrk[x] == oldrk[y] && oldrk[x + z] == oldrk[y + z]; }
void build(char ch[]) {
Mes(rk, 0), Mes(oldrk, 0);
sk = 26;
fo(i, 1, n) rk[ id[i] = i ] = ch[i] - 'a' + 1;
mysort();
for (int w = 1, p = 0; w <= n; w <<= 1, p = 0) {
fo(i, n - w + 1, n) id[ ++ p ] = i;
fo(i, 1, n) if (sa[i] > w)
id[ ++ p ] = sa[i] - w;
mysort();
mcp(oldrk, rk);
sk = 0;
fo(i, 1, n)
rk[sa[i]] = pd(sa[i], sa[i - 1], w) ? sk : ++ sk;
if (sk == n) {
fo(i, 1, n) sa[rk[i]] = i;
break;
}
}
sk = 0;
fo(i, 1, n) {
if (sk) -- sk;
while (ch[i + sk] == ch[sa[rk[i] - 1] + sk])
++ sk;
ht[rk[i]] = sk;
}
fo(i, 1, n) ft[i][0] = ht[i];
fo(j, 0, L - 1) fo(i, 1, n)
ft[i][j + 1] = (i + (1 << j) <= n) ? min(ft[i][j], ft[i + (1 << j)][j]) : ft[i][j];
}
int dt;
int get(int l, int r) {
l = rk[l], r = rk[r];
if (l > r) swap(l, r);
if (++ l == r) return ft[l][0];
dt = Log[r - l + 1];
return min(ft[l][dt], ft[r - (1 << dt) + 1][dt]);
}
} s1, s2;
struct Tree {
#define ls (t << 1)
#define rs (t << 1 | 1)
int vl[N << 2];
Tree() { Mes(vl, 0x3f); }
void Add(int t, int l, int r, int x, int y, int k, int lz) {
if (lz < vl[t]) vl[t] = lz;
if (x <= l && r <= y) {
vl[t] = k; return;
}
int mid = l + r >> 1;
if (x <= mid) Add(ls, l, mid, x, y, k, vl[t]);
if (y > mid) Add(rs, mid + 1, r, x, y, k, vl[t]);
}
void fil(int t, int l, int r, int lz, int at[]) {
if (lz < vl[t]) vl[t] = lz;
if (l == r) {
at[l] = vl[t]; return;
}
int mid = l + r >> 1;
fil(ls, l, mid, vl[t], at), fil(rs, mid + 1, r, vl[t], at);
}
#undef ls
#undef rs
} t1, t2;
int gt[N + 1][L + 1], dl[N + 1], dr[N + 1], sc[N + 1][26];
const int inf = 0x3f3f3f3f;
int sqn;
void Init() {
Log[1] = 0;
fo(i, 2, n) Log[i] = Log[i >> 1] + 1;
h1.build(998244353, 37);
h2.build(1000000007, 29);
s1.build(ch);
fd(i, (n >> 1), 1)
swap(ch[i], ch[n - i + 1]);
s2.build(ch);
fd(i, (n >> 1), 1)
swap(ch[i], ch[n - i + 1]);
int l, r, lcp, lcs, dlen, pl, pr;
fd(len, (n >> 1), 1) {
fd(k, (n / len) - 1, 1) {
l = len * k, r = len * (k + 1);
lcs = s1.get(l, r);
lcp = s2.get(n - l + 1, n - r + 1);
if (lcp + lcs > len) {
lcp = min(lcp, len), lcs = min(lcs, len);
dlen = lcp + lcs - len - 1;
t1.Add(1, 1, n, l - lcp + 1, l - lcp + 1 + dlen, (len << 1), inf);
t2.Add(1, 1, n, r + lcs - 1 - dlen, r + lcs - 1, (len << 1), inf);
}
}
}
t1.fil(1, 1, n, inf, dl), t2.fil(1, 1, n, inf, dr);
fo(i, 1, n) {
fo(j, 0, 25) sc[i][j] = sc[i - 1][j];
++ sc[i][ch[i] - 'a'];
}
fo(i, 1, n) gt[i][0] = i + dl[i] - 1;
fo(j, 0, L - 1) fo(i, 1, n)
gt[i][j + 1] = (i + (1 << j) <= n) ? min(gt[i][j], gt[i + (1 << j)][j]) : gt[i][j];
sqn = sqrt(n);
}
bool pd0(int l, int r) {
fo(i, 0, 25) if (sc[r][i] - sc[l - 1][i] > 1)
return 0;
return 1;
}
int len, sq;
bool pd1(int l, int r) {
len = r - l + 1, sq = sqrt(len);
if (check(l, r - 1, l + 1, r)) return 1;
fo(i, 2, sq) if (len % i == 0)
if (check(l, r - i, l + i, r) || check(l, r - len / i, l + len / i, r))
return 1;
return 0;
}
int min_ht;
bool pd2(int l, int r) {
len = r - l + 1;
if (dl[l] <= len || dr[r] <= len)
return 1;
fd(i, min(sqn, (len >> 1)), 1) if (check(l, l + i - 1, r - i + 1, r))
return 1;
if (sqn >= (len >> 1)) return 0;
min_ht = s1.ht[s1.rk[l]];
fd(i, s1.rk[l] - 1, s1.rk[l] - sqn + 1) {
if (i < 1) break;
if ((l + r >> 1) < s1.sa[i] && s1.sa[i] <= r && s1.sa[i] + min_ht > r)
return 1;
min_ht = min(min_ht, s1.ht[i]);
}
min_ht = len;
fo(i, s1.rk[l] + 1, s1.rk[l] + sqn - 1) {
if (i > n) break;
min_ht = min(min_ht, s1.ht[i]);
if ((l + r >> 1) < s1.sa[i] && s1.sa[i] <= r && s1.sa[i] + min_ht > r)
return 1;
}
return 0;
}
bool pd3(int l, int r) {
return (sc[r][ch[l] - 'a'] - sc[l][ch[l] - 'a'] > 0) || (sc[r - 1][ch[r] - 'a'] - sc[l - 1][ch[r] - 'a'] > 0);
}
int dx;
int get_Min_dl(int l, int r) {
if (l == r) return gt[l][0];
dx = Log[r - l + 1];
return min(gt[l][dx], gt[r - (1 << dx) + 1][dx]);
}
int main() {
scanf("%d\n%s", &n, ch + 1);
Init();
int T, l, r; read(T);
while (T --) {
read(l), read(r);
if (pd0(l, r))
puts("-1");
else
if (pd1(l, r))
puts("1");
else
if (pd2(l, r))
puts("2");
else
if (pd3(l, r) || get_Min_dl(l, r) <= r)
puts("3");
else
puts("4");
}
return 0;
}