zoukankan      html  css  js  c++  java
  • hdu6704 K-th occurrence(后缀数组+RMQ+主席树)

    题意

    给定一个长度为 (n) 的字符串,有 (q) 次询问,每次询问求字符串的 ([l, r]) 所形成的子串第 (k) 次出现时的位置。

    传送门

    思路

    ([l, r]) 形成的子串,它是一些后缀的前缀,并且这些前缀在后缀数组中的rk是相邻的,因此,对于([l, r]) 我们可以通过 rmq/lcp 求出他的区间公共前缀长度,之后二分长度大于等于 (r-l+1) 的左右端点,只需要求出这些端点的第 (k) 小就可以,对于这些端点,可以通过主席树维护 (sa[i]) ,之后就可以 (O(log)) 查询第K小了 。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    const int maxn = 1e5+10;
    const int maxm = maxn*20;
    
    int n;
    char str[maxn];
    struct node {
        int l, r, val;
    }tr[maxm];
    namespace HJTree {
        int tot = 0;
    
        void init() { tot = 0; }
    
        void build(int l, int r, int &x) {
            x = ++tot;
            tr[x].val = 0;
            if(l==r) return;
            int mid = l+r>>1;
            build(l, mid, tr[x].l);
            build(mid+1, r, tr[x].r);
        }
    
        void update(int l, int r, int x, int &y, int p) {
            y = ++tot;
    //        cout << l << " " << r << " " << x << " " << y << " " << p << endl;
            tr[y] = tr[x];
            ++tr[y].val;
            if(l==r) return;
            int mid = l+r >> 1;
            if(p <= mid) update(l, mid, tr[x].l, tr[y].l, p);
            else update(mid+1, r, tr[x].r, tr[y].r, p);
        }
    
        int query(int l, int r, int x, int y, int k) {
            if(l==r) return l;
            int mid = l+r>>1;
            int tmp = tr[tr[y].l].val - tr[tr[x].l].val;
            if(tmp >= k) return query(l, mid, tr[x].l, tr[y].l, k);
            return query(mid+1, r, tr[x].r, tr[y].r, k-tmp);
        }
    }
    
    struct SuffixArray {
        int x[maxn], y[maxn], c[maxn];
        int sa[maxn], rk[maxn], height[maxn];
    
        void SA() {
            int m = 26;
            for (int i=0; i<=m; ++i) c[i] = 0;
            for (int i=1; i<=n; ++i) ++c[(x[i] = str[i]-'a')];
            for (int i=1; i<=m; ++i) c[i] += c[i-1];
            for (int i=n; i>=1; --i) sa[c[x[i]]--] = i;
    
            for (int p, k=1; k<=n; k<<=1) {
                p = 0;
                for (int i=n; i>n-k; --i) y[++p] = i;
                for (int i=1; i<=n; ++i) {
                    if(sa[i]>k)
                        y[++p] = sa[i]-k;
                }
                for (int i=0; i<=m; ++i) c[i] = 0;
                for (int i=1; i<=n; ++i) ++c[x[y[i]]];
                for (int i=1; i<=m; ++i) c[i] += c[i-1];
                for (int i=n; i>=1; --i) sa[c[x[y[i]]]--] = y[i];
    
                p = y[sa[1]] = 1;
                for (int a, b, i=2; i<=n; ++i) {
                    a = sa[i]+k>n? -1: x[sa[i]+k];
                    b = sa[i-1]+k>n? -1: x[sa[i-1]+k];
                    y[sa[i]] = (x[sa[i]]==x[sa[i-1]] && a==b)? p: ++p;
                }
                swap(x, y);
                if(p>=n) break;
                m = p;
            }
            for (int i=1; i<=n; ++i) rk[i] = x[i];
        }
    
        void getHeight() {
            int k = 0;
            for (int i=1; i<=n; ++i) {
                if(k) --k;
                int j = sa[rk[i]-1];
                while(str[i+k] == str[j+k]) ++k;
                height[rk[i]] = k;
            }
        }
    
        int root[maxn];
        int st[maxn][20];
    
        void build() {
            SA();
            HJTree::init();
            HJTree::build(1, n, root[0]);
            for (int i=1; i<=n; ++i) HJTree::update(1, n, root[i-1], root[i], sa[i]);
        }
    
        void ST() {
            getHeight();
    //        for (int i=1; i<=n; ++i) printf("%d ", sa[i]); puts("");
    //        for (int i=1; i<=n; ++i) printf("%d ", rk[i]); puts("");
    //        for (int i=1; i<=n; ++i) printf("%d ", height[i]); puts("");
            for (int i=1; i<=n; ++i) st[i][0] = height[i];
            for (int j=1; j<=20; ++j) {
                int p = 1<<j-1, l = (1<<j)-1;
                for (int i=1; i+l<=n; ++i)
                    st[i][j] = min(st[i][j-1], st[i+p][j-1]);
            }
        }
    
        int rmq(int l, int r) {
            if(l==r) return 0x3f3f3f3f;
            ++l;
            int len = r-l+1, d=0;
            while((1<<d+1)<=len) ++d;
            int p = 1<<d;
            return min(st[l][d], st[r-p+1][d]);
        }
    
        int findL(int pos, int len) {
            int l = 1, r = pos;
            while (l <= r) {
                int mid = l + r >> 1;
                if (rmq(mid, pos) < len) l=mid+1;
                else r=mid-1;
            }
            return l;
        }
    
        int findR(int pos, int len) {
            int l = pos, r = n;
            while (l <= r) {
                int mid = l + r >> 1;
                if (rmq(pos, mid) < len) r=mid-1;
                else l=mid+1;
            }
            return r;
        }
    
        int query(int l, int r, int k) {
            int len = r-l+1;
            int pos = rk[l];
    //        cout << pos << " " << len << " ";
            int L = findL(pos, len);
            int R = findR(pos, len);
    //        cout << L << " " << R << endl;
            if(R-L+1<k) return -1;
            return HJTree::query(1, n, root[L-1], root[R], k);
        }
    }suffixArray;
    
    int T, q;
    int main() {
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &n, &q);
            scanf("%s", str+1);
            suffixArray.build();
            suffixArray.ST();
            for (int l, r, k, i=1; i<=q; ++i) {
                scanf("%d%d%d", &l, &r, &k);
                printf("%d
    ", suffixArray.query(l, r, k));
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    阻止事件冒泡
    移动端开发
    angular6 管道多参数传输 Pipe
    在div上添加小三角
    angular6 使用@Input() @Output()
    angular6 想要获取页面某些事件 如 点击 window宽高等等
    如何将项目添加到git上
    使用vue-cli 搭建element-admin后台
    bootstrap4 常用样式类名 (供自己参考)
    键盘按下 keyCode 的值
  • 原文地址:https://www.cnblogs.com/acerkoo/p/11409700.html
Copyright © 2011-2022 走看看