zoukankan      html  css  js  c++  java
  • HDU5008 Boring String Problem(后缀数组)

    练习一下字符串,做一下这道题。

    首先是关于一个字符串有多少不同子串的问题,串由小到大排起序来应该是按照sa[i]的顺序排出来的产生的。

    好像abbacd,排序出来的后缀是这样的

    1---abbacd     第一个串产生的6个前缀都是新的子串

    2---acd          第二个串除了和上一个串的前缀1 3-1=2 产生了2个子串

    3---bacd        4-0=4

    4---bbacd      5-1=4

    5---cd           2-0=0

    6---d            1-0=0

    所以所有不同的前缀应该是(len-sa[i])-lcp[i-1]的和,即串长减去与上一个串的最长公共前缀,然后求和。

    所以我们可以预处理出dp[i]表示sa[i]的后缀所产生的新串的个数,然后对dp[i]求一次前缀和,那么每次询问第k大的串的时候就可以直接lower_bound,找出串的左端和右端。但是这个(l,r)不一定是最小的,最小的可能是在sa[i+1]..sa[i+2]...里产生,所以我们首先要二分出合法的sa边界,即sa[i]....sa[j]里都可以产生的这个串,然后sa[i]...sa[j]的最小值即是我们要求的。写二分总是要跪要跪的- -0

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <string>
    #include <numeric>
    #include <cassert>
    using namespace std;
    
    #define maxn 120000
    #define ll long long
    
    struct SuffixArray
    {
        int n;
        int m[2][maxn];
        int sa[maxn];
        char s[maxn];
    
        void indexSort(int sa[], int ord[], int id[], int nId){
            static int cnt[maxn];
            memset(cnt, 0, sizeof(0)*nId);
            for (int i = 0; i < n; i++){
                cnt[id[i]]++;
            }
            partial_sum(cnt, cnt + nId, cnt);
            for (int i = n - 1; i >= 0; i--){
                sa[--cnt[id[ord[i]]]] = ord[i];
            }
        }
    
        int *id, *oId;
    
        void init(){
            n = strlen(s) + 1;
            static int w[maxn];
            for (int i = 0; i <= n; i++) w[i] = s[i];
            sort(w, w + n);
            int nId = unique(w, w + n) - w;
            id = m[0], oId = m[1];
            for (int i = 0; i < n; i++){
                id[i] = lower_bound(w, w + nId, s[i]) - w;
            }
            static int ord[maxn];
            for (int i = 0; i < n; i++){
                ord[i] = i;
            }
            indexSort(sa, ord, id, nId);
            for (int k = 1; k <= n&&nId < n; k <<= 1){
                int cur = 0;
                for (int i = n - k; i < n; i++){
                    ord[cur++] = i;
                }
                for (int i = 0; i < n; i++){
                    if (sa[i] >= k) ord[cur++] = sa[i] - k;
                }
                indexSort(sa, ord, id, nId);
                cur = 0;
                swap(oId, id);
                for (int i = 0; i < n; i++){
                    int c = sa[i], p = i ? sa[i - 1] : 0;
                    id[c] = (i == 0 || oId[c] != oId[p] || oId[c + k] != oId[p + k]) ? cur++ : cur - 1;
                }
                nId = cur;
            }
        }
    
        // lcp relevant
        int rk[maxn], lcp[maxn];
        void getlcp(){
            for (int i = 0; i < n; i++) rk[sa[i]] = i;
            int h = 0;
            lcp[0] = 0;
            for (int i = 0; i < n; i++){
                int j = sa[rk[i] - 1];
                for (h ? h-- : 0; i + h < n&&j + h < n&&s[i + h] == s[j + h]; h++);
                lcp[rk[i] - 1] = h;
            }
        }
    
        // lcp query relevant
        int d[maxn + 50][25];
        int mi[maxn+50][25];
    
        void getrmq(){
            for (int i = 0; i < n; i++) d[i][0] = lcp[i];
            for (int j = 1; (1 << j) < n; j++){
                for (int i = 0; (i + (1 << j) - 1) < n; i++){
                    d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
                }
            }
            for(int i=0;i<n;i++) mi[i][0]=sa[i];
            for (int j = 1; (1 << j) < n; j++){
                for (int i = 0; (i + (1 << j) - 1) < n; i++){
                    mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
                }
            }
        }
    
        int rmq_query3(int l,int r){
            if(l==r) return mi[l][0];
            int k=0;int len=r-l+1;
            while((1<<(k+1))<len) ++k;
            return min(mi[l][k], mi[r - (1 << k) + 1][k]);
        }
    
        int rmq_query(int l, int r){
            if(l==r) return n-1-sa[l];
            if (l > r) swap(l, r); r -= 1;
            int k = 0; int len = r - l + 1;
            while ((1 << (k + 1)) < len) k++;
            return min(d[l][k], d[r - (1 << k) + 1][k]);
        }
    
        int rmq_query2(int l, int r){
            l = rk[l], r = rk[r];
            if (l > r) swap(l, r); r -= 1;
            int k = 0; int len = r - l + 1;
            while ((1 << (k + 1)) < len) k++;
            return min(d[l][k], d[r - (1 << k) + 1][k]);
        }
    }sa;
    
    int nQ;
    ll dp[maxn];
    int n;
    int main()
    {
        while(~scanf("%s",sa.s)){
            sa.init();
            sa.getlcp();
            sa.getrmq();
            n=sa.n-1;
            dp[0]=0;
            for(int i=1;i<=n;++i){
                dp[i]=n-sa.sa[i]-sa.lcp[i-1];
                dp[i]+=dp[i-1];
            }
            ll ansl=0,ansr=0;
            ll ki;
            scanf("%d",&nQ);
            while(nQ--){
                scanf("%I64d",&ki);
                ki=(ki^ansl^ansr)+1;
                if(ki>dp[n]){
                    ansl=ansr=0;
                    printf("%d %d
    ",ansl,ansr);
                    continue;
                }
                int tl,tr;
                int id=lower_bound(dp,dp+n+1,ki)-dp;
                tl=sa.sa[id];
                tr=tl+sa.lcp[id-1]+ki-dp[id-1]-1;
                int len=tr-tl+1;
                int lf=id,rf=n;
                while(lf<rf){
                    int mid=(lf+rf+1)>>1;
                    if(sa.rmq_query(id,mid) >= len) lf=mid;
                    else rf=mid-1;
                }
                ansl=sa.rmq_query3(id,lf)+1;
                ansr=ansl+len-1;
                printf("%I64d %I64d
    ",ansl,ansr);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    第一阶段SCRUM冲刺day07
    第一阶段SCRUM冲刺day06
    第一阶段SCRUM冲刺day05
    第一阶段SCRUM冲刺day04
    2020年第七周学习进度
    第一阶段SCRUM冲刺day03
    第一阶段SCRUM冲刺day02
    团队会议01
    2020年第六周学习进度
    TCP/IP Socket 的理解
  • 原文地址:https://www.cnblogs.com/chanme/p/4000976.html
Copyright © 2011-2022 走看看