HDU - 5008 Boring String Problem (后缀数组+二分+RMQ)

Boring String Problem

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2160    Accepted Submission(s): 588


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 s i...j of the string s = a 1a 2 ...a n(1 ≤ i ≤ j ≤ n) is the string a ia i+1 ...a j. Two substrings s x...y and s z...w are cosidered to be distinct if s x...y ≠ S z...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| ≤ 10 5) with only lowercase letters.

Next line contains a postive integer q(1 ≤ q ≤ 10 5), 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 < 2 63, “⊕” 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 s l...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 s 1...n is the whole string)
 

Sample Input
  
  
aaa 4 0 2 3 5
 

Sample Output
  
  
1 1 1 3 1 2 0 0
 

Source
 

Recommend
hujie
 

题意:给你你一个字符串,求第k小的子串,,有很多询问

解题思路:后缀数组!第一次自己思考出来了。首先先统计不同的后缀对子串的数量的贡献,然后求个前缀和,通过二分可以快速找到第k小的子串!能想到这里这题就差不多了。但是这样找到的子串的下标是最大的,我们要求最小!所以我们要从L往后找最小的SA,这里通过一个RMQ维护height数组,通过二分R,可以快速定位可能有最小的下标的SA区间,,然后再通过一个RMQ查询这个区间的最小下标即可!但是这题暴力往后扫可过……证明没有100000个a这样的数据。这里给出暴力代码


#include <iostream>
#include <deque>
#include <stdio.h>
#include <map>
#include <string.h>
#include <algorithm>
#include <vector>
#include <math.h>
#include <stack>
#include <queue>
#include <set>
using namespace std;
inline void scan_d(int &ret)
{
    char c;
    ret = 0;
    while ((c = getchar()) < '0' || c > '9');
    while (c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0'), c = getchar();
    }
}


inline void scan_d(long long int &ret)
{
    char c;
    ret = 0;
    while ((c = getchar()) < '0' || c > '9');
    while (c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0'), c = getchar();
    }
}
typedef long long int ll;

const int MAXN = 100005; //用DC3要开三倍大小

int wa[MAXN], wb[MAXN], wv[MAXN], JS[MAXN]; //计算SA用的辅助数组

int rk[MAXN], height[MAXN], SA[MAXN]; //三个常用数组

/***后缀数组倍增解法***/
int 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 *SA, int n, int m)
{
    int i, j, p, *x = wa, *y = wb, *t;
    for (i = 0; i < m; i++)
        JS[i] = 0;
    for (i = 0; i < n; i++)
        JS[x[i] = r[i]]++;
    for (i = 1; i < m; i++)
        JS[i] += JS[i - 1];
    for (i = n - 1; i >= 0; i--)
        SA[--JS[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, 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++)
            JS[i] = 0;
        for (i = 0; i < n; i++)
            JS[wv[i]]++;
        for (i = 1; i < m; i++)
            JS[i] += JS[i - 1];
        for (i = n - 1; i >= 0; i--)
            SA[--JS[wv[i]]] = y[i];
        for (t = x, x = y, y = t, 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++;
    }
    return;
}
/*******************/

/***后缀数组DC3解法***/
#define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb))
#define G(x) ((x) < tb ? (x)*3 + 1 : ((x)-tb) * 3 + 2)
int c0(int *r, int a, int b)
{
    return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];
}
int c12(int k, int *r, int a, int b)
{
    if (k == 2)
        return r[a] < r[b] || r[a] == r[b] && c12(1, r, a + 1, b + 1);
    else
        return r[a] < r[b] || r[a] == r[b] && wv[a + 1] < wv[b + 1];
}
void sort(int *r, int *a, int *b, int n, int m)
{
    int i;
    for (i = 0; i < n; i++)
        wv[i] = r[a[i]];
    for (i = 0; i < m; i++)
        JS[i] = 0;
    for (i = 0; i < n; i++)
        JS[wv[i]]++;
    for (i = 1; i < m; i++)
        JS[i] += JS[i - 1];
    for (i = n - 1; i >= 0; i--)
        b[--JS[wv[i]]] = a[i];
    return;
}
void DC3(int *r, int *SA, int n, int m)
{
    int i, j, *rn = r + n, *SAn = SA + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;
    r[n] = r[n + 1] = 0;
    for (i = 0; i < n; i++)
        if (i % 3 != 0)
            wa[tbc++] = i;
    sort(r + 2, wa, wb, tbc, m);
    sort(r + 1, wb, wa, tbc, m);
    sort(r, wa, wb, tbc, m);
    for (p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
        rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;
    if (p < tbc)
        DC3(rn, SAn, tbc, p);
    else
        for (i = 0; i < tbc; i++)
            SAn[rn[i]] = i;
    for (i = 0; i < tbc; i++)
        if (SAn[i] < tb)
            wb[ta++] = SAn[i] * 3;
    if (n % 3 == 1)
        wb[ta++] = n - 1;
    sort(r, wb, wa, ta, m);
    for (i = 0; i < tbc; i++)
        wv[wb[i] = G(SAn[i])] = i;
    for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
        SA[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
    for (; i < ta; p++)
        SA[p] = wa[i++];
    for (; j < tbc; p++)
        SA[p] = wb[j++];
    return;
}
/***********************/

//计算rank和height数组
void calheight(int *r, int *SA, int n)
{
    //  memset(height,0,sizeof(height));
    //  memset(rk,0,sizeof(rk));
    int i, j, k = 0;
    for (i = 1; i <= n; i++)
        rk[SA[i]] = i;
    for (i = 0; i < n; height[rk[i++]] = k)
        for (k ? k-- : 0, j = SA[rk[i] - 1]; r[i + k] == r[j + k]; k++)
            ;
}
int N;         //字符串长度
/*****RMQ部分*****/
int ST[MAXN][16];
void RMQ(int *a)
{
    int m = (int)(log(N * 1.0) / log(2.0));
    for (int i = 1; i <= N; i++)
        ST[i][0] = a[i];
    for (int j = 1; j <= m; j++)
        for (int i = 1; i + (1 << j) - 1 <= N; i++)
            ST[i][j] = min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
}
int query(int a, int b)
{
    int k = int(log(b - a + 1.0) / log(2.0));
    return min(ST[a][k], ST[b - (1 << k) + 1][k]);
}


char sstr[MAXN]; //主字符串
int str[MAXN];
ll subnum[MAXN];

bool judge(int m,ll k){
    if(subnum[m]<k)
        return true;
    else
        return false;
}


int main()
{

    while (~scanf("%s", sstr))
    {
        N=strlen(sstr);
        for (int i = 0; i < N ; i++)
            str[i] = sstr[i] - 'a' + 1;

        str[N] = 0; //必须要末尾补0!!!!!

        DA(str, SA, N + 1, 28);//N是没有补0的大小,算SA时要把末尾0计算进去,所以要N+1
        calheight(str, SA, N);//计算height时不用末尾0
        RMQ(height);

        //cout<<query(2,5)<<endl;
        //调试
//        for(int i=0;i<=N+1;i++)
//            cout<<SA[i]<<" ";
//        cout<<endl;
//        for(int i=0;i<=N+1;i++)
//            cout<<rk[i]<<" ";
//        cout<<endl;
//        for(int i=0;i<=N+1;i++)
//            cout<<height[i]<<" ";
//        cout<<endl;

        subnum[0]=0;
        for(int i=1;i<=N;i++){
            subnum[i]=subnum[i-1]+N-SA[i]-height[i];
           // cout<<subnum[i]<<" ";
        }
        int q;
        scan_d(q);
        ll v,k;
        ll l=0,r=0;
        int m;
        for(int i=0;i<q;i++){
            scan_d(v);

            k=(l^r^v)+1;

            if(k>subnum[N]){
                printf("%d %d\n",0,0);
                l=0,r=0;
                continue;
             }


          //  k=v;
            l=1;
            r=N;
            while(l<r){
                m=(l+r)/2;
                if(judge(m,k)){
                    l=m+1;
                }
                else
                    r=m;
            }

            int len=k-subnum[l-1]+height[l];
            // cout<<l<<endl;
            int L=SA[l]+1;
            int R=L+len-1;
           // cout<<L<<" "<<R<<endl;

            int ii;
            int lll=l;
            for(ii=l+1;ii<=N;ii++){
                if(height[ii]<len)
                    break;
                else{
                    if(SA[ii]<SA[lll])
                        lll=ii;
                }
            }

            l=SA[lll]+1;
            r=l+len-1;

            printf("%I64d %I64d\n",l,r);
        }


    }

    return 0;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值