hdu5008 Boring String Problem 后缀数组+二分+RMQ

来源:互联网 发布:菜鸟网络嘉定 编辑:程序博客网 时间:2024/03/29 16:03

      题意大概就是给一个串,每次查询q,令k=(q^l^r)+1,其中^表示异或,l,r是上次查询的答案,初始为0,去串中字典序第k小的子串最靠左出现的位置。之前做过一题 SPOJ 的SUBLEX基本和这题一样,不过那题只要找到第k小的字符串就行了,这题需要找到这个子串最靠右出现的位置。考虑找到第k小的子串,直接拿原串先构造后缀数组,统计一下第i个后缀有多少个不同的前缀num[i](也就是在原串中有多少个不重复的子串),按sa排序后,这些连续出现的子串的字典序也是相同的,那么对num[i]求前缀和后就可以去二分一个位置,找到字典序第k小的子串出现的位置了。这里找到的位置不一定是最靠左的,所以还要在原串中找一下最左的位置,其实到了这里,直接两个指针,一个向前一个向后,暴力找到最长的连续的height[i]>=目标子串长度 的位置下标L,R,然后RMQ(L,R,sa)找到最小的位置就好了,当然极限数组(例如10W个a)肯定会TLE的,但它居然能过...而且还更快一点,果然是数据太弱了- =...当然,本着严(zi)谨(nue)的态度,好孩子不要学这种方法。一种可行的做法,确定了当前的位置pos,我们要做的就是在pos后面找个R,前面找个L使得[L,R]这个区间的height的最小值>=目标子串的长度,那么可以直接在[POS,n]中二分处位置R,同样在[1,POS]中二分出L,由于RMQ预处理后查询是O(1),所以两边logN的二分就可以找到答案的区间[L,R],最后再RMQ一下就得到最后的答案了。注意这里求区间的RMQ和求答案的RMQ是查询的两个数组,要分别初始化...

/*=============================================================================#  Author:Erich#  FileName:=============================================================================*/#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <cstring>#include <string>#include <vector>#include <map>#include <queue>#include <stack>#define lson id<<1,l,m#define rson id<<1|1,m+1,rusing namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const ll INF=1ll<<60;const double PI=acos(-1.0);const int maxn=205000;const int mod=1e9+7;char ss[maxn];int belong[maxn];int s[maxn],rs[maxn];int sa[maxn],t[maxn],t2[maxn],c[maxn];int n,m,tt;int rank[maxn],height[maxn];int len,l;inline int idx(char c){return c-'a'+1;}inline char fdx(int x){return char(x-1+'a');}void getheight(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 build_ss(int m,int n){    n++;    int i,*x=t,*y=t2;    for (int i=0; i<m; i++) c[i]=0;    for (int i=0; i<n; i++) c[x[i]=s[i]]++;    for (int i=1; i<m; i++) c[i]+=c[i-1];    for (int 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=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]]=(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;    }}int d[2][maxn][50];int LOG[maxn];int RMQ_init(int x,int A[]){for(int i=1; i<=n; i++) d[x][i][0]=A[i];for (int j=1; (1<<j)<=n; j++)for (int i=1; i+(1<<j)-1<=n; i++)d[x][i][j]=min(d[x][i][j-1],d[x][i+(1<<(j-1))][j-1]);}int RMQ(int x,int L,int R){int k=LOG[R-L+1];return min(d[x][L][k],d[x][R-(1<<k)+1][k]);}ll num[maxn];int main(){//    freopen("in.txt","r",stdin);    int k=0;    for (int i=0; i<105000; i++)    {        while((1<<(k+1))<=i) k++;        LOG[i]=k;    }while(~scanf("%s",ss)){int l=strlen(ss);for(int i=0; i<l; i++)s[i]=idx(ss[i]);n=l;s[n]=0;build_ss(33,n);getheight(n);for (int i=0; i<=n; i++)num[i]=n-sa[i];for (int i=1; i<=n; i++)num[i]-=height[i];for (int i=1; i<=n; i++)num[i]+=num[i-1];ll tot=num[n];scanf("%d",&m);ll la=0,lb=0;ll k;RMQ_init(0,height);RMQ_init(1,sa);while(m--){scanf("%I64d",&k);k^=la;k^=lb;k++;if (k>=1 && k<=tot){int pos=lower_bound(num+1,num+1+n,k)-num;int len=k-num[pos-1]+height[pos];int l=pos+1,r=n;int mid;int L,R;while(l<r){    mid=(l+r)>>1;    if (RMQ(0,pos+1,mid)>=len) l=mid+1;    else r=mid;}if (RMQ(0,pos+1,l)>=len) R=l;else R=l-1;l=1; r=pos;while(l<r){    mid=(l+r)>>1;    if (RMQ(0,mid,pos)>=len) r=mid;    else l=mid+1;}if (RMQ(0,l,pos)>=len) L=l-1;else L=l;la=RMQ(1,L,R);lb=la+len-1;la++;lb++;printf("%I64d %I64d\n",la,lb);}else{la=lb=0;puts("0 0");}}}return 0;}


0 0
原创粉丝点击