Codeforces

Luogu

分析

如果 $[l, r]$ 内不存在两个相同字符,则无解,否则答案至多为 $4$。

于是我们可以对答案进行讨论:

  • 如果答案为 $1$,说明子串形如 $AAAA\cdots$,因此我们可以枚举 $r - l + 1$ 的约数 $d$,并判断其是否存在长度为 $d$ 的周期。
  • 如果答案为 $2$,有三种情况:$AAB$、$ABA$、$BAA$。对于第一种和第三种,我们需要求以 $l$ 开头 / 以 $r$ 结尾的最短的 $AA$ 串,不妨设为 $L_l$ 和 $R_r$;对于第二种,我们需要判断 $ABA$ 是否存在 border。
  • 如果答案为 $3$,有三种情况:$ABAC$、$BAAC$、$BACA$。对于第一种和第三种,显然 $A$ 取一个字符最好,因此我们只需要判断开头 / 结尾的字符是否出现了两次;对于第二种,我们需要判断 $[l, r]$ 中是否存在 $AA$ 串,即判断 $\min_{l \leq i \leq r} \{ L_i \}$ 是否 $\leq r$。
  • 否则答案为 $4$。

下面考虑如何实现。

首先我们对于每种字符,求出其出现次数的前缀和,这样子就可以判断无解和 $ABAC$、$BACA$。

然后我们对原串求后缀数组,从而实现求两个后缀的 LCP,这样子就可以方便的判断周期。

我们还需要求出 $L_i$ 和 $R_i$,使用 NOI2016 优秀的拆分 中的方法,每次相当于一个区间对一个等差数列取 $\min$,可以使用线段树维护。这样子同时可以求出区间 $L_i$ 的最小值。

最后还剩下一个判断区间 border 的问题。有一个 $\mathcal{O}(\sqrt{n})$ 求出区间最短 border 的方法:首先判断是否有长度为 $1 \sim \sqrt{n}$ 的 border,如果没有,则最短 border 与原串的后缀排名差 $\leq \sqrt{n}$(证明略)。这样子可以做到 $\mathcal{O}(\sqrt{n})$ 的枚举量。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
typedef long long ll;

int read() {
    int X = 0, w = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    while (c >= '0' && c <= '9') X = X * 10 + c - '0', c = getchar();
    return X * w;
}

const int N = 200000 + 10;
const int inf = 0x3f3f3f3f;

int n, Q; char s[N];
int sum[N][26];

struct SuffixArray {
    int sa[N], rnk[N], height[N], x[N], y[N], z[N];
    void SuffixSort() {
        int m = 256;
        for (int i = 1; i <= n; ++i) ++z[x[i] = s[i]];
        for (int i = 2; i <= m; ++i) z[i] += z[i - 1];
        for (int i = n; i; --i) sa[z[x[i]]--] = i;
        for (int k = 1; k < n; k <<= 1) {
            int p = 0;
            for (int i = n - k + 1; i <= n; ++i) y[++p] = i;
            for (int i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k;
            for (int i = 1; i <= m; ++i) z[i] = 0;
            for (int i = 1; i <= n; ++i) ++z[x[i]];
            for (int i = 2; i <= m; ++i) z[i] += z[i - 1];
            for (int i = n; i; --i) sa[z[x[y[i]]]--] = y[i], y[i] = 0;
            swap(x, y), x[sa[1]] = p = 1;
            for (int i = 2; i <= n; ++i)
                x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) ? p : ++p;
            if (p == m) break;
            m = p;
        }
    }
    void GetHeight() {
        for (int i = 1; i <= n; ++i) rnk[sa[i]] = i;
        for (int i = 1, j = 0; i <= n; ++i) {
            if (j) --j;
            int p = sa[rnk[i] - 1];
            while (s[i + j] == s[p + j]) ++j;
            height[rnk[i]] = j;
        }
    }

    int lg[N], st[18][N];
    void buildST() {
        for (int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
        for (int i = 1; i <= n; ++i) st[0][i] = height[i];
        for (int i = 1; i < 18; ++i)
            for (int j = 1; j <= n; ++j)
                st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
    }
    int qmin(int l, int r) {
        int t = lg[r - l + 1];
        return min(st[t][l], st[t][r - (1 << t) + 1]);
    }
    int LCP(int x, int y) {
        if (rnk[x] > rnk[y]) swap(x, y);
        return qmin(rnk[x] + 1, rnk[y]);
    }
} A, B;

#define ls (o << 1)
#define rs (o << 1 | 1)
struct SegmentTree {
    int minv[N << 2], tag[N << 2];

    SegmentTree() { memset(minv, 0x3f, sizeof(minv)), memset(tag, 0x3f, sizeof(tag)); }

    void update(int o, int l, int w) { tag[o] = min(tag[o], w), minv[o] = min(minv[o], l + w); }
    void pushup(int o) { minv[o] = min(minv[ls], minv[rs]); }
    void pushdown(int o, int l, int r) {
        int mid = (l + r) >> 1;
        if (tag[o] != inf) update(ls, l, tag[o]), update(rs, mid + 1, tag[o]), tag[o] = inf;
    }

    void modify(int o, int l, int r, int ql, int qr, int w) {
        if (ql <= l && r <= qr) { update(o, l, w); return; }
        int mid = (l + r) >> 1; pushdown(o, l, r);
        if (ql <= mid) modify(ls, l, mid, ql, qr, w);
        if (qr > mid) modify(rs, mid + 1, r, ql, qr, w);
        pushup(o);
    }
    int query(int o, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) return minv[o];
        int mid = (l + r) >> 1, res = inf; pushdown(o, l, r);
        if (ql <= mid) res = min(res, query(ls, l, mid, ql, qr));
        if (qr > mid) res = min(res, query(rs, mid + 1, r, ql, qr));
        return res;
    }
} L, R;
#undef ls
#undef rs

void InitAA(SuffixArray &lcp_sa, SuffixArray &lcs_sa, SegmentTree &T) {
    for (int l = 1; l <= n / 2; ++l)
        for (int i = l, j = l << 1; j <= n; i += l, j += l) {
            int lcp = lcp_sa.LCP(i, j), lcs = lcs_sa.LCP(n - i + 1, n - j + 1);
            if (lcp + lcs - 1 >= l) T.modify(1, 1, n, i - lcs + 1, i + lcp - l, 2 * l - 1);
        }
}

bool CheckBorder(int l, int r) {
    int len = r - l + 1, S = sqrt(n) + 1;
    for (int i = 1; i < len && i <= S; ++i)
        if (A.LCP(l, r - i + 1) >= i) return 1;
    for (int i = A.rnk[l] - 1, h = A.height[i + 1], c = 0; i && c <= S && h > S; ++c, h = min(h, A.height[i--]))
        if (A.sa[i] >= l && A.sa[i] <= r && A.sa[i] + h > r) return 1;
    for (int i = A.rnk[l] + 1, h = A.height[i], c = 0; i <= n && c <= S && h > S; ++c, h = min(h, A.height[++i]))
        if (A.sa[i] >= l && A.sa[i] <= r && A.sa[i] + h > r) return 1;
    return 0;
}

bool CheckNS(int l, int r) {
    int flag = 1;
    for (int i = 0; i < 26; ++i)
        if (sum[r][i] - sum[l - 1][i] >= 2) { flag = 0; break; }
    return flag;
}
bool Check1(int l, int r) {
    int len = r - l + 1;
    for (int i = 1; i * i <= len; ++i) {
        if (len % i) continue;
        if (A.LCP(l, l + i) >= len - i) return 1;
        if (i != 1 && A.LCP(l, l + len / i) >= len - len / i) return 1;
    }
    return 0;
}
bool Check2(int l, int r) {
    if (CheckBorder(l, r)) return 1;
    if (L.query(1, 1, n, l, l) <= r) return 1;
    if (R.query(1, 1, n, n - r + 1, n - r + 1) <= n - l + 1) return 1;
    return 0;
}
bool Check3(int l, int r) {
    if (sum[r][s[l] - 'a'] - sum[l][s[l] - 'a']) return 1;
    if (sum[r - 1][s[r] - 'a'] - sum[l - 1][s[r] - 'a']) return 1;
    if (L.query(1, 1, n, l, r) <= r) return 1;
    return 0;
}

int main() {
    n = read(); scanf("%s", s + 1);
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j < 26; ++j)
            sum[i][j] = sum[i - 1][j] + (j == s[i] - 'a');
    A.SuffixSort(), A.GetHeight(), A.buildST();
    reverse(s + 1, s + n + 1);
    B.SuffixSort(), B.GetHeight(), B.buildST();
    reverse(s + 1, s + n + 1);
    InitAA(A, B, L), InitAA(B, A, R);
    Q = read();
    while (Q--) {
        int l = read(), r = read();
        if (CheckNS(l, r)) { puts("-1"); continue; }
        if (Check1(l, r)) { puts("1"); continue; }
        if (Check2(l, r)) { puts("2"); continue; }
        if (Check3(l, r)) { puts("3"); continue; }
        puts("4");
    }
    return 0;
}
最后修改:2021 年 03 月 06 日 07 : 33 PM