SP7258 Lexicographical Substring Search 题解


题面

本质不同第 \(k\) 小。对 SAM 上每个点求一个 \(b_i\) 表示从这个点开始往后走能走到的本质不同的子串个数。然后拿 \(k\) 在自动机上跑即可。

点击查看代码
const int N=9e4+13;
char s[N];
int nxt[N<<1],len[N<<1],ptot=1,lastpos=1,c[N<<1],ind[N<<1],a[N<<1],ans[N];
ll b[N<<1];
std::unordered_map son[N<<1],zrzak;
inline int newpos(std::unordered_map nson,int nlen){return len[++ptot]=nlen,std::swap(son[ptot],nson),ptot;}
inline void insert(int c){
	int p=lastpos,u=newpos(zrzak,len[p]+1);
	while(p&&son[p].find(c)==son[p].end()) son[p][c]=u,p=nxt[p];
	lastpos=u;
	if(!p) return nxt[u]=1,void();
	int d=son[p][c];
	if(len[d]==len[p]+1) nxt[u]=d;
	else{
		int v=newpos(son[d],len[p]+1);
		nxt[v]=nxt[d],nxt[d]=nxt[u]=v;
		while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
	}
}
inline void inittopo(){
	std::queue q;q.push(1);
	for(int i=1;i<=ptot;++i)
		for(int j=0;j<26;++j)
			if(son[i].find(j)!=son[i].end()) ind[son[i][j]]++;
	int tot=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		a[++tot]=u;
		for(int i=0;i<26;++i)
			if(son[u].find(i)!=son[u].end())
				if(!(--ind[son[u][i]])) q.push(son[u][i]);
	}
}
int main(){
	read(s+1);int n=strlen(s+1);
	for(int i=1;i<=n;++i) insert(s[i]-'a');
	for(int i=2;i<=ptot;++i) c[i]=1;
	inittopo();
	for(int i=ptot;i;--i){
		int u=a[i];b[u]=c[u];
		for(int j=0;j<26;++j)
			if(son[u].find(j)!=son[u].end()) b[u]+=b[son[u][j]];
	}
	int q;read(q);
	while(q--){
		int now=1,k,l=0;read(k);
		while(1){
			if(k<=c[now]) break;
			k-=c[now];
			for(int j=0;j<26;++j){
				if(son[now].find(j)==son[now].end()) continue;
				if(k<=b[son[now][j]]){ans[++l]=j,now=son[now][j];break;}
				k-=b[son[now][j]];
			}
		}
		if(!l){println(-1);continue;}
		for(int i=1;i<=l;++i) print((char)(ans[i]+'a'));
		print('\n');
	}
	return 0;
}