[TJOI2015]弦论

我们先求出该字符串的(SA)(Ht)

然后分类讨论

(T=0)时,每次去掉(Ht)往后扫就行

(T=1)时,我们考虑(lcp)对答案的影响

既然用到(lcp),那就要用(ST)表维护

左端点固定时,随右端点的增大,区间(min)单调不升

我们就可以用二分+限制右端点的方式统计某个后缀的前缀的贡献

循环遍历每个后缀时,由于(Ht)部分已经统计过对答案的贡献,就不用再计算了,从(Ht+1)的位置继续向后统计即可

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
using namespace std;

const int MAXN=5e5+5;

int ln,maxn,p,t;
int SA[MAXN],id[MAXN],rnk[MAXN],bnk[MAXN],Ht[MAXN];
char ch[MAXN];
int ST[19][MAXN],lg[MAXN];

void shel()
{
	for(int i=1;i<=ln;++i) ++bnk[rnk[id[i]]];
	for(int i=1;i<=maxn;++i) bnk[i]+=bnk[i-1];
	for(int i=1;i<=ln;++i) SA[++bnk[rnk[id[i]]-1]]=id[i];
	for(int i=0;i<=maxn;++i) bnk[i]=0;
	return;
}

void GetSA()
{
	for(int i=1;i<=ln;++i) rnk[i]=ch[i],id[i]=i,maxn=max(maxn,rnk[i]);
	shel();
	for(int k=1;k<ln;k<<=1){
		for(int i=1;i<=k;++i) id[i]=ln-k+i;
		int ct=k;
		for(int i=1;i<=ln;++i) if(SA[i]>k) id[++ct]=SA[i]-k;
		shel();swap(id,rnk);rnk[SA[1]]=1;
		for(int i=2;i<=ln;++i) rnk[SA[i]]=(id[SA[i]]==id[SA[i-1]]&&id[SA[i]+k]==id[SA[i-1]+k])?rnk[SA[i-1]]:rnk[SA[i-1]]+1;
		if(rnk[SA[ln]]==ln) return;
		maxn=rnk[SA[ln]];
	}return;
}

void GetHt()
{
	int k=0;
	for(int i=1;i<=ln;++i){
		if(rnk[i]==1) continue;
		if(k) --k;
		int ct=SA[rnk[i]-1];
		while(ct+k<=ln&&i+k<=ln&&ch[i+k]==ch[ct+k]) ++k;
		Ht[rnk[i]]=k;
	}return;
}

int Getmi(int l,int r){int tmp=lg[r-l+1];return min(ST[tmp][l+(1<<tmp)-1],ST[tmp][r]);}

void slv1()
{
	for(int i=1;i<=ln;++i) lg[i]=i>>lg[i-1]+1?lg[i-1]+1:lg[i-1];
	for(int i=1;i<=ln;++i) ST[0][i]=Ht[i];
	for(int j=1;j<=lg[ln];++j){
		int tmp=1<<j-1;
		for(int i=tmp<<1;i<=ln;++i){
			ST[j][i]=min(ST[j-1][i],ST[j-1][i-tmp]);
		}
	}for(int i=1;i<=ln;++i){
		int tmp=SA[i]+Ht[i];
		int ct=ln;
		while(1){
			if(tmp>ln) break;
			if(ct==i){
				if(t>ln-tmp+1){t-=ln-tmp+1;break;}
				for(int j=SA[i];j<=tmp+t-1;++j) putchar(ch[j]);
				return;
			}int l=i,r=ct;
			while(l<r){
				int mid=l+r+1>>1;
				if(Getmi(i+1,mid)>=tmp-SA[i]+1) l=mid;
				else r=mid-1;
			}ct=r;
			if(t>r-i+1) t-=r-i+1;
			else{
				for(int j=SA[i];j<=tmp;++j) putchar(ch[j]);
				return;
			}++tmp;
		}
	}puts("-1");
	return;
}

void slv2()
{
	int ct=1;
	while(t>ln-SA[ct]-Ht[ct]+1) t-=ln-SA[ct]-Ht[ct]+1,++ct;
	if(ct>ln){puts("-1");return;}
	for(int i=SA[ct];i<=SA[ct]+t+Ht[ct]-1;++i) putchar(ch[i]);
	puts("");
	return;
}

int main()
{
	scanf("%s",ch+1);ln=strlen(ch+1);
	GetSA();GetHt();scanf("%d%d",&p,&t);
	p?slv1():slv2();
	return 0;
}
原文地址:https://www.cnblogs.com/AH2002/p/10223659.html