HDU5008--Boring String Problem(SA+二分)

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
思路:每個sa[i]會貢獻的新子串數爲len-sa[i]-height[i];
用前綴和搞出來,那麼很容易求出一個l,r滿足爲所求子串(但是此時不是序號最小)
這裏有個地方要想清楚,序號的更新,這個串要麼爲sa[i]的一部分,要麼爲sa[>i]的一部分。不可能在sa[<i]。
不然就不是sa[i]貢獻的新子串了。。所以只需二分右端點。。最後就是RMQ求最小序號了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 240080
#define inf 0x3f3f3f3f
#define LL long long int
int str[maxn],vis[maxn];
char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn],key[maxn];
int height[maxn],Rank[maxn];
int len1,len2;
int Min[maxn][20],lca[maxn][20];
LL dp[maxn];
inline int max(int a,int b)
{
    return a>b?a:b;
}
/*
用SA模板注意在最後添加一個比所有字符都小的字符。
key[n] = 0;
build_sa(key,n+1,m);
getHeight(key,n+1);
顯然sa[0] 就是最後那個位置。。。
height[i] 表示 sa[i] 和 sa[i-1] 的最長公共前綴。。
*/

void build_sa(int * s,int n,int m)
{
    int i,*x = t,*y = t2;
    for(i = 0;i < m;i++)    c[i] = 0;
    for(i = 0;i < n;i++)    c[ x[i] = s[i] ]++;
    for(i = 1;i < m;i++)    c[i] += c[i-1];
    for(i = n-1;i >= 0;i--)    sa[--c[x[i]]] = i;
    for(int k = 1;k <= n;k <<= 1)
    {
        int p = 0;
        for(i = n - k;i < n;i++)    y[p++] = i;
        for(i = 0;i < n;i++)    if(sa[i] >= k)    y[p++] = sa[i] - k;
        for(i = 0;i < m;i++)    c[i] = 0;
        for(i = 0;i < n;i++)    c[ x[y[i]] ]++;
        for(i = 0;i < m;i++)    c[i] += c[i-1];
        for(i = n-1;i >= 0;i--)    sa[--c[x[y[i]]]] = y[i];
        //根據sa和y數組計算新的數y組
        swap(x,y);
        p = 1;    x[sa[0]] = 0;
        for(i = 1;i < n;i++)
            x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1:p++;
        if(p >= n)    break;
        m = p;
    }
}

void getHeight(int * s,int n)
{
    int i,j,k = 0;
    for(i = 0;i < n;i++)    Rank[sa[i]] = i;
    for(i = 0;i < n;i++)    
    {
        if(k) k--;
        int j = sa[Rank[i]-1];
        while(s[i+k] == s[j+k])        k++;
        height[Rank[i]] = k;
    }
}

void RMQ_INIT(int n)//求lca
{
    for(int i = 1;i < n;i++)    lca[i][0] = height[i];
	for(int j = 1;(1<<j)<=n;j++)
	{
		for(int i = 0;i+(1<<j)-1<n;i++)
		{
			lca[i][j] = min(lca[i][j-1],lca[i+(1<<(j-1))][j-1]);
		}
	}
}

int RMQ_Query(int l,int r)
{
    int k = 0;
    while((1<<(k+1) <= r-l+1)) k++;
    return min(lca[l][k],lca[r-(1<<k)+1][k]);
}
void RMQ_INIT1(int n)//求區間最小,這是求出sa後求得
{
    for(int i = 1;i < n;i++)    key[i] = sa[i]+1;
    for(int i = 1;i < n;i++)    Min[i][0] = key[i];
    for(int j = 1;(1<<j)<=n;j++)
	{
		for(int i = 0;i+(1<<j)-1<n;i++)
		{
			Min[i][j] = min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
		}
	}
}
int RMQ_Query1(int l,int r)//
{
    int k = 0;
    while((1<<(k+1)) <= r-l+1) k++;
    return min(Min[l][k],Min[r-(1<<k)+1][k]);
}
int main()
{
   // freopen("in.txt","r",stdin);
    while(scanf("%s",s)!=EOF)
    {
        int len = strlen(s);
        for(int i = 0;i < len;i++)
            str[i] = s[i]-'a'+2;
        str[len] = 0;
        build_sa(str,len+1,30);
        getHeight(str,len+1);
        dp[0] = 0;
        dp[1] = len-sa[1];
        for(int i = 2;i <= len;i++)
        {
            LL add = len-sa[i]-height[i];
            dp[i] = dp[i-1] + add;
        }
        RMQ_INIT(len+1);
        RMQ_INIT1(len+1);
        LL l = 0,r = 0,v;
        int q;    scanf("%d",&q);
        while(q--)
        {
            scanf("%I64d",&v);
            LL k = (l^r^v)+1;
            if(k > dp[len])
            {
                l = r = 0;
                cout << 0 << " " << 0 << endl;
                continue;
            }
            int pos = lower_bound(dp+1,dp+1+len,k)-dp;
            k -= dp[pos-1];
            int L = sa[pos];
            int R = L+k+height[pos]-1;////非常沒問題
            //這樣從L 到 R就是所要求的字符串,但是還不是滿足最小的序號的
            int Len = R-L+1;
            int ll = pos+1,rr = len;
            int ans = pos;
            while(ll <= rr)
            {
                int mid = (ll+rr)>>1;
                if(RMQ_Query(pos+1,mid) < Len) rr = mid-1;
                else 
                {
                    ans = mid;
                    ll = mid+1;
                }
            }
            int fuck = RMQ_Query1(pos,ans);
            l = fuck,r = fuck+Len-1;
            printf("%I64d %I64d\n",l,r);
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章