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

    Problem Description
    In this problem, you are given a string s and q queries.

    For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest.

    A substring si...j of the string s = a1a2 ...an(1 ≤ i ≤ j ≤ n) is the string aiai+1 ...aj. Two substrings sx...y and sz...w are cosidered to be distinct if sx...y ≠ Sz...w
     
    Input
    The input consists of multiple test cases.Please process till EOF.

    Each test case begins with a line containing a string s(|s| ≤ 105) with only lowercase letters.

    Next line contains a postive integer q(1 ≤ q ≤ 105), the number of questions.

    q queries are given in the next q lines. Every line contains an integer v. You should calculate the k by k = (l⊕r⊕v)+1(l, r is the output of previous question, at the beginning of each case l = r = 0, 0 < k < 263, “⊕” denotes exclusive or)
     
    Output
    For each test case, output consists of q lines, the i-th line contains two integers l, r which is the answer to the i-th query. (The answer l,r satisfies that sl...r is the k-th smallest and if there are several l,r available, ouput l,r which with the smallest l. If there is no l,r satisfied, output “0 0”. Note that s1...n is the whole string)
     
    Sample Input
    aaa
    4
    0
    2
    3
    5
     
    Sample Output
    1 1
    1 3
    1 2
    0 0
     
    题意:找到字符串中第k小的字串,并且输出下标最小的那一个。
     
    解析:用后缀数组处理,每个后缀不同的子串个数是len-sa[i]-h[i],所以可以二分去找,但是找到了并不一定是下标最小的,所以还要继续往后面更新。
     
    代码:
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    typedef __int64 LL;
    typedef pair<int,int> par;
    const int maxn=100005;
    char S[maxn];
    int A[maxn];
    LL sum[maxn];
    int wa[maxn],wb[maxn],wv[maxn],WS[maxn],sa[maxn];
    bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; }
    void DA(int *r,int n,int m)  //模板
    {
        int i,j,p;
        int *x=wa,*y=wb;
        for(i=0;i<m;i++) WS[i]=0;
        for(i=0;i<n;i++) WS[x[i]=r[i]]++;
        for(i=1;i<m;i++) WS[i]+=WS[i-1];
        for(i=n-1;i>=0;i--) sa[--WS[x[i]]]=i;
    
        for(p=1,j=1;p<n;j<<=1,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) WS[i]=0;
            for(i=0;i<n;i++) WS[wv[i]]++;
            for(i=1;i<m;i++) WS[i]+=WS[i-1];
            for(i=n-1;i>=0;i--) sa[--WS[wv[i]]]=y[i];
            swap(x,y);
            for(p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    int rk[maxn],h[maxn];
    void GetHeight(int *r,int n)
    {
        for(int i=0;i<=n;i++) rk[sa[i]]=i;
        int k=0;
        h[0]=0;
        for(int i=0;i<n;i++)
        {
            if(k) k--;  //先减1
            int j=sa[rk[i]-1];//排名在前面的
            while(r[i+k]==r[j+k]) k++; //相同一直加
            h[rk[i]]=k;
        }
    }
    int BIS(int x,int y,LL k)//找到第一个大于k的位置
    {
        while(x<=y)
        {
            int mid=(x+y)/2;
            if(sum[mid]>=k) y=mid-1;
            else x=mid+1;
        }
        return y+1;
    }
    int main()
    {
        while(scanf("%s",S)!=EOF)
        {
            int len=strlen(S);
            for(int i=0;i<len;i++) A[i]=S[i];
            A[len]=0;
            DA(A,len+1,130); //后缀数组处理
            GetHeight(A,len); //得到lcp值
            sum[0]=0;
            for(int i=1;i<=len;i++)
            {
                int a=sa[i];
                int b=h[i];
                sum[i]=sum[i-1]+len-a-b; //计算不同子串个数
            }
            int Q;
            LL l=0,r=0,v;
            scanf("%d",&Q);
            while(Q--)
            {
                scanf("%lld",&v);
                LL k=(l^r^v)+1;
                if(k>sum[len]) printf("%lld %lld
    ",l=0,r=0); //无解的情况
                else
                {
                    int p=BIS(1,len,k); //二分找
                    l=sa[p];
                    int sl=h[p]+k-sum[p-1]; //长度
                    int cur=p;
                    while(++cur<=len&&h[cur]>=sl) l=min(l,(LL)sa[cur]);//往后面找位置最小的,不知道这种方式为何不会超时
                    r=l+sl-1;
                    l++; r++;
                    printf("%lld %lld
    ",l,r);
                }
            }
        }
        return 0;
    }
    View Code
     
     
  • 相关阅读:
    C++11中右值引用和移动语义
    面试题3:自己实现单链表
    C++中指针和引用、数组之间的区别
    C++中对象模型
    C++中虚函数的动态绑定和多态性
    C++11中多线程库
    C++中友元
    C++中迭代器原理、失效和简单实现
    C++11中智能指针的原理、使用、实现
    C++中模板与泛型编程
  • 原文地址:https://www.cnblogs.com/wust-ouyangli/p/5745626.html
Copyright © 2011-2022 走看看