「解题报告」HDU6815 Funny String

很傻啊,模拟赛啥都想不到,很傻啊。

简单字符串练习题。好像咋做都能做。

首先考虑在开头加,在开头加 \(c\) 实际上仅增加了一个 \(cS\) 的后缀,那么我们只需要知道 \(cS\) 在所有后缀中排多少即可。我们先求出 \(S\) 的后缀数组,然后直接二分找即可。或者有一个更好写的方法,就是直接枚举每一个后缀,然后看它去掉第一个字符后与 \(S\) 的字典序关系,这样枚举一遍就能知道每个 \(c\) 的答案。对于其它的串,如果它的排名在 \(cS\) 的排名之后就加 \(1\) 即可。

然后考虑在结尾加。首先把查询 \(n+1\) 判掉,这时候排名显然是开头字母小于 \(c\) 的数量 \(+1\)。这时候发现排名更改了很多,直接维护不太可能。我们考虑只计算出排名的变化,那么就是考虑原来比这个后缀小的后缀有多少比它大了,原来比这个后缀大的后缀有多少比它小了。

对于前者,发现这种情况当且仅当一个长为 \(i\) 的后缀:

  • 是当前这个串的前缀;
  • 这个串的第 \(i+1\) 个字符比 \(c\) 小。

由于都是同一个串的后缀,这个后缀一定是整个串的后缀,而且它还是这个串的前缀,说明这一定是原串的 Border,那么考虑反串跑一遍 KMP,那么我们就是要统计这个点到根有多少串满足条件。直接主席树就能做,或者可以根据 Border Series 的理论直接拆成 \(O(\log n)\) 个等差数列,同一个等差数列里面的下一个字符肯定相等,直接跑就行。

对于后者,发现这种情况当且仅当当前串是某一个后缀的前缀,且下一个字符比 \(c\) 小。容易发现这样的串一定是连续的一段区间(后缀排序后),直接二分即可。

那这就做完了。

全程用 SAM 好像也能做,就是值域比较寄,需要开 map 存。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n, m, q, op;
int s[MAXN];
int t[MAXN];
int sa[MAXN], tmp[MAXN], cnt[MAXN], rk[MAXN];
void getSuffixArray(int s[], int n) {
    int m = ::m;
    for (int i = 1; i <= m; i++) cnt[i] = 0;
    for (int i = 1; i <= n; i++) cnt[rk[i] = s[i]]++;
    for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; i--) sa[cnt[s[i]]--] = i;
    for (int w = 1, t = 0; ; w <<= 1, m = t, t = 0) {
        for (int i = 1; i <= w; i++) tmp[++t] = n - w + i;
        for (int i = 1; i <= n; i++) if (sa[i] > w) tmp[++t] = sa[i] - w;
        for (int i = 1; i <= m; i++) cnt[i] = 0;
        for (int i = 1; i <= n; i++) cnt[rk[tmp[i]]]++;
        for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; i--) sa[cnt[rk[tmp[i]]]--] = tmp[i];
        t = 0; swap(tmp, rk);
        for (int i = 1; i <= n; i++) 
            rk[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + w] == tmp[sa[i - 1] + w]) ? t : ++t;
        if (t == n) {
            for (int i = 1; i <= n; i++) sa[rk[i]] = i;
            break;
        }
    }
}
int height[MAXN];
int st[MAXN][22];
void getHeight() {
    for (int i = 1, j = 0; i <= n; i++) {
        if (j) j--;
        while (s[i + j] == s[sa[rk[i] - 1] + j]) j++;
        height[rk[i]] = j;
    }
}
void init() {
    for (int i = 1; i <= n; i++) st[i][0] = height[i];
    for (int j = 1; j <= 20; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int lcp(int i, int j) {
    if (i > j) swap(i, j);
    i++;
    int len = __lg(j - i + 1);
    return min(st[i][len], st[j - (1 << len) + 1][len]);
}
int pos[MAXN], loc[MAXN];
struct SegmentTree {
    struct Node {
        int lc, rc, v;
    } t[MAXN * 42];
    int tot;
    void add(int d, int &p, int l = 1, int r = m) {
        if (!p) p = ++tot, t[p].v++;
        else t[++tot] = t[p], p = tot, t[p].v++;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (d <= mid) add(d, t[p].lc, l, mid);
        else add(d, t[p].rc, mid + 1, r);
    } 
    int query(int a, int b, int p, int l = 1, int r = m) {
        if (a > b) return 0;
        if (!p) return 0;
        if (a <= l && r <= b) return t[p].v;
        int mid = (l + r) >> 1;
        if (b <= mid) return query(a, b, t[p].lc, l, mid);
        if (a > mid) return query(a, b, t[p].rc, mid + 1, r);
        return query(a, b, t[p].lc, l, mid) + query(a, b, t[p].rc, mid + 1, r);
    }
} tree;
int root[MAXN];
int nxt[MAXN];
int main() {
    scanf("%d%d%d%d", &n, &m, &q, &op);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i]);
    }
    getSuffixArray(s, n);
    getHeight();
    init();
    memset(loc, -1, sizeof loc);
    for (int i = 1; i <= n; i++) {
        int c = s[sa[i]];
        int l;
        if (sa[i] + 1 == n) l = 0;
        else l = lcp(rk[sa[i] + 1], rk[1]);
        if (s[sa[i] + 1 + l] < s[l + 1]) {
            pos[c] = i + 1;
        }
        loc[c] = i;
    }
    loc[0] = 0;
    for (int i = 1; i <= m; i++) if (!pos[i]) {
        if (loc[i - 1] != -1) pos[i] = loc[i - 1] + 1;
        else pos[i] = pos[i - 1];
    }
    for (int i = 1; i <= m; i++) cnt[i] = 0;
    for (int i = 1; i <= n; i++) cnt[s[i]]++;
    for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
    nxt[n] = n + 1;
    for (int i = n - 1, j = n + 1; i >= 1; i--) {
        while (j != n + 1 && s[j - 1] != s[i]) j = nxt[j];
        if (s[j - 1] == s[i]) j--;
        nxt[i] = j;
    }
    for (int i = n; i >= 1; i--) if (nxt[i] != n + 1) {
        root[i] = root[nxt[i]];
        int c = s[n - (nxt[i] - i) + 1];
        tree.add(c, root[i]);
    }
    while (q--) {
        int x, y, z; scanf("%d%d%d", &x, &y, &z);
        if (x == 1) {
            if (z == 1) {
                printf("%d\n", pos[y]);
            } else {
                int ans = rk[z - 1];
                if (ans >= pos[y]) ans++;
                printf("%d\n", ans);
            }
        } else {
            if (z == n + 1) {
                printf("%d\n", cnt[y - 1] + 1);
            } else {
                long long ans = rk[z];
                ans -= tree.query(1, y - 1, root[z]);
                int l = rk[z], r = n;
                while (l < r) {
                    int mid = (l + r + 1) >> 1;
                    if (lcp(rk[z], mid) == n - z + 1 && s[sa[mid] + (n - z + 1)] < y) l = mid;
                    else r = mid - 1;
                }
                ans += l - rk[z];
                if (ans >= cnt[y - 1] + 1) ans++;
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}
posted @ 2023-06-13 15:30  APJifengc  阅读(45)  评论(0编辑  收藏  举报