【SPOJ 7258】Lexicographical Substring Search

http://www.spoj.com/problems/SUBLEX/
好难啊。
建出后缀自动机,然后在后缀自动机的每个状态上记录通过这个状态能走到的不同子串的数量。该状态能走到的所有状态的f值的和+1就是当前状态的f值。
最后对于询问的k,从root开始走顺便加加减减就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int in() {
	int k = 0; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar());
	for(; c >= '0' && c <= '9'; c = getchar())
		k = k * 10 + c - 48;
	return k;
}

int tot = 0, par[250003], go[250003][26], val[250003], f[250003], root, last;

void extend(int w) {
	int p = last;
	int np = ++tot; val[np] = val[p] + 1;
	while (p && go[p][w] == 0)
		go[p][w] = np, p = par[p];
	if (p == 0) par[np] = root;
	else {
		int q = go[p][w];
		if (val[q] == val[p] + 1) par[np] = q;
		else {
			int nq = ++tot; val[nq] = val[p] + 1;
			memcpy(go[nq], go[q], sizeof(go[q]));
			par[nq] = par[q];
			par[q] = par[np] = nq;
			while (p && go[p][w] == q)
				go[p][w] = nq, p = par[p];
		}
	}
	last = np;
}

char s[150003];
int len, Q, k, c[150003], id[250003], tmp;

int main() {
	root = last = ++tot;
	scanf("%s", s + 1);
	len = strlen(s + 1);
	for(int i = 1; i <= len; ++i)
		extend(s[i] - 'a');
	
	for(int i = 1; i <= tot; ++i)
		++c[val[i]];
	for(int i = 1; i <= len; ++i)
		c[i] += c[i - 1];
	for(int i = tot; i >= 1; --i)
		id[c[val[i]]--] = i;
	
	int sum, x;
	for(int i = tot; i >= 1; --i) {
		sum = 0; x = id[i];
		for(int j = 0; j < 26; ++j)
			sum += f[go[x][j]];
		f[x] = sum + 1;
	}
	
	Q = in();
	while (Q--) {
		k = in();
		tmp = root;
		while (k) {
			for(int i = 0; i < 26; ++i)
				if (x = go[tmp][i])
					if (f[x] >= k) {
						putchar('a' + i);
						--k;
						tmp = x;
						break;
					} else
						k -= f[x];
		}
		puts("");
	}
	return 0;
}
posted @ 2016-10-06 17:21  abclzr  阅读(246)  评论(0编辑  收藏  举报