2019CCPC网络赛 1003.K-th occurrence(Sam线段树合并+倍增优化)

一句话题解:用线段树合并维护right集,对于每个串记录在sam里的位置,倍增跳到合适的节点之后在那个节点的线段树里查询kth。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;

int _, n, q, l, r, k;
char s[maxn];

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int endpos[maxn << 1], pos[maxn];
    int a[maxn], b[maxn << 1];
    int sz, last, root;

    int rmq[maxn << 1][21];

    int tree[maxn * 50], ls[maxn * 50], rs[maxn * 50], tot;

    void update(int &i, int l, int r, int pos) {
        if (!i) {
            i = ++tot;
            tree[i] = ls[i] = rs[i] = 0;
        }
        if (l == r) {
            ++tree[i];
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            update(ls[i], l, mid, pos);
        } else {
            update(rs[i], mid + 1, r, pos);
        }
        tree[i] = tree[ls[i]] + tree[rs[i]];
    }

    int merge(int x, int y) {
        if (!x || !y) {
            return x + y;
        }
        int z = ++tot;
        ls[z] = merge(ls[x], ls[y]);
        rs[z] = merge(rs[x], rs[y]);
        tree[z] = tree[ls[z]] + tree[rs[z]];
        return z;
    }

    int query(int i, int l, int r, int k) {
        if (!i) {
            return 0;
        }
        if (l == r) {
            return l;
        }
        int mid = (l + r) >> 1;
        if (tree[ls[i]] >= k) {
            return query(ls[i], l, mid, k);
        }
        if (tree[rs[i]] >= k - tree[ls[i]]) {
            return query(rs[i], mid + 1, r, k - tree[ls[i]]);
        }
        return -1;
    }

    void init() {
        //如多次建立自动机,加入memset操作
        root = sz = last = 1;
        memset(a, 0, sizeof(a));
        tot = 0;
        memset(next[root], 0, sizeof(next[root]));
    }

    void add(int c, int id) {
        int p = last;
        int np = ++sz;
        last = np;

        endpos[np] = 0;
        update(endpos[np], 1, n, id);
        memset(next[np], 0, sizeof(next[np]));
        step[np] = step[p] + 1;
        while (!next[p][c] && p) {
            next[p][c] = np;
            p = link[p];
        }

        if (p == 0) {
            link[np] = root;
        } else {
            int q = next[p][c];
            if (step[p] + 1 == step[q]) {
                link[np] = q;
            } else {
                int nq = ++sz;
                endpos[nq] = 0;
                memcpy(next[nq], next[q], sizeof(next[q]));
                step[nq] = step[p] + 1;
                link[nq] = link[q];
                link[q] = link[np] = nq;
                while (next[p][c] == q && p) {
                    next[p][c] = nq;
                    p = link[p];
                }
            }
        }
    }

    void build() {
        init();
        for (int i = 1; i <= n; i++) {
            add(s[i] - 'a', i);
            pos[i] = last;
        }
        for (int i = root; i <= sz; ++i) {
            rmq[i][0] = link[i];
        }
        for (int j = 1; j <= 20; ++j) {
            for (int i = root; i <= sz; ++i) {
                rmq[i][j] = rmq[rmq[i][j - 1]][j - 1];
            }
        }
        for (int i = 1; i <= sz; i++) {
            a[step[i]]++;
        }
        for (int i = 1; i <= step[last]; i++) {
            a[i] += a[i - 1];
        }
        for (int i = 1; i <= sz; i++) {
            b[a[step[i]]--] = i;
        }
        for (int i = sz; i > root; --i) {
            int e = b[i];
            endpos[link[e]] = merge(endpos[link[e]], endpos[e]);
        }
    }

    void solve() {
        int p = pos[r], len = r - l + 1;
        for (int i = 20; ~i; --i) {
            if (step[rmq[p][i]] >= len) {
                p = rmq[p][i];
            }
        }
        int ans = query(endpos[p], 1, n, k);
        if (ans != -1) {
            ans -= (len - 1);
        }
        printf("%d\n", ans);
    }

} sam;

int main() {
    scanf("%d", &_);
    while (_--) {
        scanf("%d%d%s", &n, &q, s + 1);
        sam.build();
        while (q--) {
            scanf("%d%d%d", &l, &r, &k);
            sam.solve();
        }
    }
    return 0;
}
发布了156 篇原创文章 · 获赞 20 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/Cymbals/article/details/100046701