hdu 5008 Boring String Problem

      后缀数组+RMQ+二分。 先跑后缀数组,然后求出一组非优解。然后利用height数组,求出能搞出满足串的rank范围(二分RMQ,求出右端点),然后在rank范围RMQ一下sa数组,找出最小的左端点就可以了。

      后缀数组模板摘自:http://blog.csdn.net/dr5459/article/details/9051149

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))
#define lowbit(x) (x&(-x))
using namespace std;

#ifdef __int64
typedef __int64 LL;
#else
typedef long long LL;
#endif

const int maxn=211111;
/******************************************************************
**  后缀数组 Suffix Array
**  INIT:solver.call_fun(char* s);
**  SP_USE: solver.LCS(char *s1,char* s2); //最长公共字串
******************************************************************/
struct SuffixArray
{
    int r[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    int t[maxn],t2[maxn],c[maxn],n;
    int m;///模板长度
    void init(char* s)
    {
        n=strlen(s);
        for (int i=0; i<n; i++) r[i]=int(s[i]);
        m=300;
    }
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    /**
    字符要先转化为正整数
    待排序的字符串放在r[]数组中,从r[0]到r[n-1],长度为n,且最大值小于m。
    所有的r[i]都大于0,r[n]无意义算法中置0
    函数结束后,结果放在sa[]数组中(名次从1..n),从sa[1]到sa[n]。s[0]无意义
    **/
    void build_sa()
    {
        int i,k,p,*x=t,*y=t2;
        r[n++]=0;
        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[i]=r[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 (k=1,p=1; k<n; k*=2,m=p)
        {
            for (p=0,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=1; i<m; i++) c[i]+=c[i-1];
            for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for (i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
        }
        n--;
    }
    /**
    height[2..n]:height[i]保存的是lcp(sa[i],sa[i-1])
    rank[0..n-1]:rank[i]保存的是原串中suffix[i]的名次
    **/
    void getHeight()
    {
        int i,j,k=0;
        for (i=1; i<=n; i++) rank[sa[i]]=i;
        for (i=0; i<n; i++)
        {
            if (k) k--;
            j=sa[rank[i]-1];
            while (r[i+k]==r[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    int d[maxn][20];
    ///元素从1编号到n
    void RMQ_init(int A[],int n)
    {
        for (int i=1; i<=n; i++) d[i][0]=A[i];
        for (int j=1; (1<<j)<=n; j++)
            for (int i=1; i+(1<<(j-1))<=n; i++)
                d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
    }
    int RMQ(int L,int R)
    {
        int k=0;
        if(L>R) swap(L,R);
        if(L == R) return n - sa[L];
        L++;
//        k = log2(R - L + 1);
        while ((1<<(k+1))<=R-L+1) k++;
        return min(d[L][k],d[R-(1<<k)+1][k]);
    }

    int d2[maxn][20];
    void RMQ_init2(int A[],int n)
    {
        for (int i=1; i<=n; i++) d2[i][0]=A[i];
        for (int j=1; (1<<j)<=n; j++)
            for (int i=1; i+(1<<(j-1))<=n; i++)
                d2[i][j]=min(d2[i][j-1],d2[i+(1<<(j-1))][j-1]);
    }
    int RMQ2(int L,int R)
    {
        int k=0;
        if(L>R) swap(L,R);
//        k = log2(R - L + 1);
        while ((1<<(k+1))<=R-L+1) k++;
        return min(d2[L][k],d2[R-(1<<k)+1][k]);
    }
    void LCP_init()
    {
        RMQ_init(height,n);
        RMQ_init2(sa,n);
    }
    void call_fun(char* s)
    {
        init(s);//初始化后缀数组
        build_sa();//构造后缀数组sa
        getHeight();//计算height与rank
        LCP_init();//初始化RMQ
    }
    LL ans[maxn];
    int solve(int n)
    {
        LL tot=0;
        for(int i=1; i<=n; i++)
        {
            tot+=(n-sa[i]-height[i]);
            ans[i] = tot;
        }
        return tot;
    }

    void query(LL k, int &L, int &R)
    {
        int l = 1, r = n;
        if(ans[n] < k)
        {
            L = 0; R = 0;
            return ;
        }
        while(l <= r)
        {
            int m = (l + r) >> 1;
            if(ans[m] <= k) l = m + 1;
            else r = m - 1;
        }
        if(k == ans[r])
        {
            L = sa[r] + 1; R = n;
        }
        else
        {
            l = sa[r + 1] + 1;
            k -= ans[r];
            k --;
            r = l + k + height[r + 1];
            L = l; R = r;
        }
        l = rank[L - 1]; r = n;
        while(l <= r)
        {
            int m = (l + r) >> 1;
            if(RMQ(rank[L - 1], m) >= R - L + 1)l = m + 1;
            else r = m - 1;
        }
        l = RMQ2(rank[L - 1], r);
        R = R - L + l + 1;
        L = l + 1;
    }
} sol;

char ch[maxn];

int main()
{
    while(scanf("%s",ch) != EOF)
    {
        int n=strlen(ch);
        sol.call_fun(ch);
        sol.solve(n);
        int q;
        scanf("%d", &q);
        int l = 0, r = 0;
        while(q --)
        {
            LL k;
            scanf("%I64d", &k);
            LL ll = l, rr = r, kk = k;
            k = ll ^ rr ^ k;
            sol.query(k + 1, l, r);
            printf("%d %d\n", l, r);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值