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
     
     
  • 相关阅读:
    网络应用框架Apache MINA 一个应用样例(转)
    Linux安装JDK详细步骤(转)
    MySQL主从复制配置
    linux IP 设置
    Log4j的应用实例
    linux 远程复制
    MySQL外部访问配置
    Camshift算法2
    Canny边缘检测
    cvCopy()和cvCloneImage()的区别
  • 原文地址:https://www.cnblogs.com/wust-ouyangli/p/5745626.html
Copyright © 2011-2022 走看看