[2019CCPC网络赛][hdu6704]K-th occurrence(后缀数组&主席树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6704

题意为查询子串s[l...r]第k次出现的位置。

写完博客后5分钟的更新


写完博客才发现这份代码和杭电的代码查重了....

为什么会变成这样呢?是我先交的,明明是我先交的…


激动!!第一次网络赛做出这种(板子)题。

首先求一下后缀数组的Height,我们知道Height数组表示两个排名相邻的后缀的最长公共前缀,则Height数组的区间最小值即为区间排名相邻的后缀的最长公共前缀。

我们想知道那些后缀包含了所查询的区间s[l...r]这样的前缀,则先找到s[l...n]这个后缀的排名,然后在包含这个排名的Height数组中找到最长的区间,且该区间最小值要大于等于r-l+1。即该区间内的后缀的最长公共前缀包含s[l...r]。

找这个最长的区间就可以分别二分左右端点,判断用ST表预处理Height数组,然后O(1)查询即可。

然后我们的任务就是在这个区间内找到第k小的位置。这个问题就可以对后缀数组的排名建主席树。然后查询即可。

#include<bits/stdc++.h>
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 110;
int sa[maxn], tax[maxn], rak[maxn], tp[maxn], Height[maxn], lg[maxn];
int dp[maxn][20];
int root[maxn], ls[maxn * 40], rs[maxn * 40], val[maxn * 40], cnt;
char s[maxn];
void Qsort(int n, int m) {
    for (int i = 0; i < m; i++) tax[i] = 0;
    for (int i = 0; i < n; i++) tax[rak[i]]++;
    for (int i = 0; i < m; i++) tax[i] += tax[i - 1];
    for (int i = n - 1; i >= 0; i--) sa[--tax[rak[tp[i]]]] = tp[i];
}
void suffix(int n, int m) {//n为原串长度+1,m为原串的种类
    for (int i = 0; i < n; i++)
        rak[i] = s[i], tp[i] = i;
    Qsort(n, m);
    //debug();
    for (int k = 1; k <= n; k <<= 1) {
        int p = 0;
        for (int i = n - k; i < n; i++) tp[p++] = i;
        for (int i = 0; i < n; i++) if (sa[i] >= k) tp[p++] = sa[i] - k;
        Qsort(n, m);
        swap(rak, tp);
        p = 1;
        rak[sa[0]] = 0;
        for (int i = 1; i < n; i++)
            rak[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + k] == tp[sa[i] + k]) ? p - 1 : p++;
        if (p >= n)
            break;
        m = p;
    }
}
void getH(int n) {
    int j, k = 0;
    for (int i = 0; i <= n; i++)
        rak[sa[i]] = i;
    for (int i = 0; i < n; i++) {
        if (k)k--;
        j = sa[rak[i] - 1];
        while (s[i + k] == s[j + k])
            k++;
        Height[rak[i]] = k;
    }
}
void RMQ(int n) {
    for (int i = 1; i <= n; i++)
        dp[i][0] = Height[i];
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
    }
    lg[0] = -1;
    for (int i = 1; i <= n; i++) {
        if ((i&(i - 1)) == 0)
            lg[i] = lg[i - 1] + 1;
        else
            lg[i] = lg[i - 1];
    }
}
int queryR(int l, int r) {
    int k = lg[r - l + 1];
    return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}
void build(int l, int r, int &i) {
    i = ++cnt;
    val[i] = 0;
    if (l == r)
        return;
    int mid = l + r >> 1;
    build(l, mid, ls[i]);
    build(mid + 1, r, rs[i]);
}
void update(int k, int l, int r, int &i) {
    ls[++cnt] = ls[i], rs[cnt] = rs[i], val[cnt] = val[i] + 1;
    i = cnt;
    if (l == r)
        return;
    int mid = l + r >> 1;
    if (k <= mid)
        update(k, l, mid, ls[i]);
    else
        update(k, mid + 1, r, rs[i]);
}
int query(int u, int v, int k, int l, int r) {
    if (l == r)
        return l;
    int x = val[ls[v]] - val[ls[u]];
    int mid = l + r >> 1;
    if (x >= k)
        return query(ls[u], ls[v], k, l, mid);
    else
        return query(rs[u], rs[v], k - x, mid + 1, r);
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, q, cnt = 0;
        scanf("%d%d", &n, &q);
        scanf("%s", s);
        suffix(n + 1, 256);
        getH(n);
        for (int i = 1; i <= n; i++)
            ++sa[i];
        for (int i = n; i >= 1; i--)
            rak[i] = rak[i - 1];
        RMQ(n);
        build(1, n, root[0]);
        for (int i = 1; i <= n; i++) {
            root[i] = root[i - 1];
            update(sa[i], 1, n, root[i]);
        }
        //for (int i = 1; i <= n; i++)
        //    printf("%d%c", sa[i], i == n ? '\n' : ' ');
        //for (int i = 1; i <= n; i++)
        //    printf("%d%c", rak[i], i == n ? '\n' : ' ');
        //for (int i = 1; i <= n; i++)
        //    printf("%d%c", Height[i], i == n ? '\n' : ' ');
        while (q--) {
            int l, r, k, len;
            scanf("%d%d%d", &l, &r, &k);
            len = r - l + 1;
            int x = rak[l], y = rak[l];
            int L = x + 1, R = n;
            while (L <= R) {
                int mid = L + R >> 1;
                if (queryR(L, mid) >= len)
                    y = mid, L = mid + 1;
                else
                    R = mid - 1;
            }
            L = 2, R = x;
            while (L <= R) {
                int mid = L + R >> 1;
                if (queryR(mid, R) >= len)
                    x = mid - 1, R = mid - 1;
                else
                    L = mid + 1;
            }
            //cout << x << " " << y << endl;
            if (y - x + 1 < k)
                printf("-1\n");
            else
                printf("%d\n", query(root[x - 1], root[y], k, 1, n));
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/sainsist/p/11409152.html