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\n",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\n",l,r);
            }
        }
    }
    return 0;
}
View Code

 

 
 
posted @ 2016-08-07 10:18  wust_ouyangli  阅读(243)  评论(0编辑  收藏  举报