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

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)

const int N=1e5+10;

char s[N];
int sa[N],t[N],t2[N],c[N];
void build_sa(int n,int m){
	int *x=t,*y=t2;
	rep(i,0,m)c[i]=0;
	rep(i,0,n)c[x[i]=s[i]]++;
	rep(i,1,m)c[i]+=c[i-1];
	per(i,0,n)sa[--c[x[i]]]=i;

	for(int k=1;k<=n;k<<=1){
		int p=0;
		rep(i,n-k,n)y[p++]=i;
		rep(i,0,n)if(sa[i]>=k)y[p++]=sa[i]-k;

		rep(i,0,m)c[i]=0;
		rep(i,0,n)c[x[y[i]]]++;
		rep(i,0,m)c[i]+=c[i-1];
		per(i,0,n)sa[--c[x[y[i]]]]=y[i];

		swap(x,y);
		p=1;x[sa[0]]=0;
		rep(i,1,n)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 rk[N],height[N];
void getHeight(int n){
	int k=0;
	rep(i,0,n)rk[sa[i]]=i;
	rep(i,0,n){
		if(k)k--;
		int j=sa[rk[i]-1];//注意不要有rk[i]=0,所以我们通常在末尾添加一个 最小值,让他把0拿走 
		while(s[i+k]==s[j+k])k++;
		height[rk[i]]=k;
	}
}
LL sum[N];

int len;
void get_ans(LL& l,LL& r,LL K){
	if(K>sum[len]){
		l=r=0;
		return;
	}
	int pos=lower_bound(sum,sum+len+1,K)-sum;

	K-=sum[pos-1];

	l=sa[pos];
	r =sa[pos]+height[pos]+K;

	int tlen=r-l;
	pos++;
	while(pos<=len&&height[pos]>=tlen){
		if(sa[pos]<l){
			l=sa[pos];
			r=l+tlen;
		}
		pos++;
	}
	l++,r;
}

int main(){
	while(scanf("%s",s)==1){
		len=strlen(s);
		s[len]=0;

		build_sa(len+1,200);
		getHeight(len+1);
		
		//pos=0的位置的字符,属于添加,不计算 
		rep(i,1,len+1)sum[i]=sum[i-1]+len-sa[i]-height[i];

		int m;
		scanf("%d",&m);
		LL l=0,r=0;
		while(m--){
			LL K;
			scanf("%lld",&K);
			K=(l^r^K)+1LL;
		//	printf("l:%lld r:%lld KKK:%lld\n",l,r,K);
			get_ans(l,r,K);
			printf("%lld %lld\n",l,r);
		}
	}
	return 0;
}

 

RMQ+二分

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)

//后缀数组
//begin
//sa[i] 排名第i的为 S[sa[i]---len)这个后缀, sa[]从0开始,一般我们添加 极小字符,使略过0,从1开始
//height[i] 为 sa[i]和sa[i-1]的LCP 

//rk[i] 为 下标为i的字符串的实际rank 
const int N=1e5+10;
int r[N];//需先将字符串 转换成 int 类型;末尾添加最小字符
int sa[N],t[N],t2[N],c[N]; 
void build_sa(int n,int m){
	int *x=t,*y=t2;
	rep(i,0,m)c[i]=0;
	rep(i,0,n)c[x[i]=r[i]]++;
	rep(i,1,m)c[i]+=c[i-1];
	per(i,0,n)sa[--c[x[i]]]=i;

	for(int k=1;k<=n;k<<=1){
		int p=0;
		rep(i,n-k,n)y[p++]=i;
		rep(i,0,n)if(sa[i]>=k)y[p++]=sa[i]-k;

		rep(i,0,m)c[i]=0;
		rep(i,0,n)c[x[y[i]]]++;
		rep(i,0,m)c[i]+=c[i-1];
		per(i,0,n)sa[--c[x[y[i]]]]=y[i];

		swap(x,y);
		p=1;x[sa[0]]=0;
		rep(i,1,n)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 rk[N],height[N];
void getHeight(int n){
	int k=0;  //跳过最小的 后面的添加字符 
	rep(i,0,n)rk[sa[i]]=i;
	rep(i,0,n-1){//最后的那个不要计算,否则RE 
		if(k)k--;
		int j=sa[rk[i]-1];//注意不要有rk[i]=0,所以我们通常在末尾添加一个 最小值,让他把0拿走
		while(r[i+k]==r[j+k])k++;
		height[rk[i]]=k;
	}
}

int Log[N];
int dp_heigh[N][20],dp_pos[N][20];//一个是位置,一个是区域最小值
void get_rmq(int n,int X[],int (*dp)[20]){
    rep(i,1,n)dp[i][0]=X[i];//初始化每个点
    for(int j=1;j<=Log[n];++j){
        for(int i=1;i+(1LL<<j)-1<n;++i){
            dp[i][j]=min(dp[i][j-1],dp[i+(1LL<<(j-1))][j-1]);
        }
    }
}
int RMQ(int (*dp)[20],int L,int R){//返回区域内的最小值 ,pos,和 val
    if(L>R)swap(L,R);
    int tmp=Log[R-L+1];
    //printf("L:%d R:%d tmp:%d\n",L,R-(1LL<<tmp)+1,tmp);
    return min(dp[L][tmp],dp[R-(1LL<<tmp)+1][tmp]);
}
//end


LL sum[N];
int len;
//枚举的效率,很低,我们可以rmq找到 一段区间内 都是>=Len的位置。
void get_ans(LL& L,LL& R,LL K){
	if(K>sum[len]){L=R=0;return;}
	
	int pos=lower_bound(sum,sum+len+1,K)-sum;
	K-=sum[pos-1];

	L=sa[pos];//我们找到了按照 后缀排序的最前面的值,但不一定是 物理上最左面的值
	R=sa[pos]+height[pos]+K-1;
    int tlen=R-L+1;
    
    int Max_Rig=pos;
    int l=pos+1,r=len,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(RMQ(dp_heigh,pos+1,mid)>=tlen){
            Max_Rig=mid; l=mid+1;
        }
        else
            r=mid-1;
    }
    L=RMQ(dp_pos,pos,Max_Rig);
    R=L+tlen;
    ++L;
}

char s[N];
int main(){

    Log[1]=0;
    rep(i,2,N)Log[i]=Log[i/2]+1;

	while(scanf("%s",s)==1){
		len=strlen(s);

		rep(i,0,len)r[i]=s[i]-'a'+1;
		r[len]=0;//在末尾添加一个 最小的字符,日常操作(防止get_height里面RE)

		build_sa(len+1,27);
		getHeight(len+1);

        get_rmq(len+1,height,dp_heigh);
        get_rmq(len+1,sa,dp_pos);

		//排名第一 的位置的字符,属于添加,不计算
		rep(i,1,len+1)sum[i]=sum[i-1]+len-sa[i]-height[i];
        
		int m;
		scanf("%d",&m);
		LL l=0,r=0;
		while(m--){
			LL K;
			scanf("%lld",&K);
			K=(l^r^K)+1LL;
			get_ans(l,r,K);
			printf("%lld %lld\n",l,r);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值