@spoj


@description@

给定一个由小写字母构成的字符串。
多次询问。询问它相异子串中字典序第 K 小的子串。

input
第一行给出字符串 S(长度小于等于 90000)。
第二行给出 Q,表示询问个数。Q <= 500。
接下来 Q 行,每行一个 K,表示询问字典序第 K 小的子串。

output
输出 Q 行,第 i 行输出第 i 个询问的答案。

sample input
aaa
2
2
3
sample output
aa
aaa

@solution@

子串必须要不相同。那就只好用后缀自动机咯。

我们知道,后缀自动机的主体部分 DAWG 是一个有向无环图。
我们还知道,后缀自动机中每一个结点表示若干子串。

因此,我们可以求出从某一结点出发能够表示多少子串,记为 size。
假如我们从初始结点寻找字典序最小的子串,肯定是沿着字典序最小的边走。经过上面的处理后,我们可以判断经过这个结点一定能得到字典序最小,第二小,...,第 size 小的子串。
我们要寻找的第 K 小,就可以判断一下是否经过这个结点,如果是就走这个结点,否则寻找下一条相应的边。

@accepted code@

#include<cstdio>
#include<cstring>
const int MAXN = 90000;
typedef long long ll;
struct node{
	node *ch[26], *fa; int mx;
	node *nxt; ll siz;
}pl[2*MAXN + 5], *bin[MAXN + 5], *tcnt, *root, *lst;
void init() {
	tcnt = root = lst = &pl[0];
}
node *newnode() {
	tcnt++;
	return tcnt;
}
void add_bin(node *x) {
	x->nxt = bin[x->mx];
	bin[x->mx] = x;
}
void clone(node *x, node *y) {
	for(int i=0;i<26;i++)
		x->ch[i] = y->ch[i];
	x->fa = y->fa;
}
void extend(int x) {
	node *cur = newnode(), *p = lst;
	cur->mx = lst->mx + 1; lst = cur;
	add_bin(cur);
	while( p && !p->ch[x] )
		p->ch[x] = cur, p = p->fa;
	if( !p )
		cur->fa = root;
	else {
		node *q = p->ch[x];
		if( p->mx + 1 == q->mx )
			cur->fa = q;
		else {
			node *nq = newnode();
			clone(nq, q); nq->mx = p->mx + 1;
			add_bin(nq);
			cur->fa = q->fa = nq;
			while( p && p->ch[x] == q )
				p->ch[x] = nq, p = p->fa;
		}
	}
}
char s[MAXN + 5];
int main() {
	init(); scanf("%s", s);
	int lens = strlen(s);
	for(int i=0;i<lens;i++)
		extend(s[i] - 'a');
	for(int i=lens;i>=1;i--)
		while( bin[i] ) {
			bin[i]->siz = 1;
			for(int j=0;j<26;j++)
				if( bin[i]->ch[j] ) bin[i]->siz += bin[i]->ch[j]->siz;
			bin[i] = bin[i]->nxt;
		}
	int Q; scanf("%d", &Q);
	for(int i=1;i<=Q;i++) {
		int K; scanf("%d", &K);
		node *nw = root;
		while( K ) {
			for(int i=0;i<26;i++)
				if( nw->ch[i] ) {
					if( K > nw->ch[i]->siz )
						K -= nw->ch[i]->siz;
					else {
						printf("%c", i + 'a');
						nw = nw->ch[i]; K--;
						break;
					}
				}
		}
		puts("");
	}
}

@details@

事实上,如果你处理出每个子串的出现次数(这也是后缀自动机常做的事情),也可以根据这个思路求解可以重复的第 K 小子串问题。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10254769.html