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
     
     
  • 相关阅读:
    oracle重命名数据库
    GridView分页的实现
    vb发post信息,非常简单,就一个函数
    第六周进度条
    软件工程作业
    进度条第五周
    四则运算设计03
    进度条03
    单元测试课堂作业
    个人作业02
  • 原文地址:https://www.cnblogs.com/wust-ouyangli/p/5745626.html
Copyright © 2011-2022 走看看