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
     
     
  • 相关阅读:
    Install JDK In Ubuntu
    js初学—实现checkbox全选功能
    js初学—js全自定义单选框
    poj1679The Unique MST(次小生成树模板)
    hdu3486Interviewe(二分是错的)(ST算法RMQ + 判定上下界枚举)
    poj3974 Palindrome(Manacher最长回文)
    poj1734 Sightseeing trip(Floyd求无向图最小环)
    BZOJ1123 [POI2008]BLO(割点判断 + 点双联通缩点size)
    2.2 logistic回归
    神经网络的火热
  • 原文地址:https://www.cnblogs.com/wust-ouyangli/p/5745626.html
Copyright © 2011-2022 走看看