SPOJ_7258 SUBLEX
这个题目可以先用O(N)的时间构造出后缀自动机,由于后缀自动机中每条路径都对应着一个不重复的子串,因此可以用dp处理出到达一个节点以及其后面的节点的路径数,然后每次查询时遍历一遍后缀自动机就可以了。
但这样总的查询复杂度是O(N*Q)的,如果不用各种常数优化的话很容易TLE。
#include<stdio.h> #include<string.h> #define MAXD 90010 struct SufAuto { int pre, next[26], len; }sa[2 * MAXD]; namespace SA { int node, tail; void init() { node = tail = 0; sa[0].pre = -1; } void insert(int k, int len) { int p = tail, np = ++ node; sa[np].len = len; for(; p != -1 && !sa[p].next[k]; p = sa[p].pre) sa[p].next[k] = np; tail = np; if(p == -1) sa[np].pre = 0; else { if(sa[sa[p].next[k]].len == sa[p].len + 1) sa[np].pre = sa[p].next[k]; else { int q = sa[p].next[k], r = ++ node; sa[r] = sa[q], sa[r].len = sa[p].len + 1; sa[q].pre = sa[np].pre = r; for(; p != -1 && sa[p].next[k] == q; p = sa[p].pre) sa[p].next[k] = r; } } } } char b[MAXD]; int N, q[2 * MAXD], h[MAXD], f[2 * MAXD], num[2 * MAXD][26], next[2 * MAXD][26]; char ch[2 * MAXD][26]; void init() { int i; SA::init(); for(i = 0; b[i]; i ++) SA::insert(b[i] - 'a', i + 1); N = i; } void solve() { int i, j, k, x, cur, t, rear, n; for(i = 0; i <= N; i ++) h[i] = 0; for(i = 1; i <= SA::node; i ++) ++ h[sa[i].len]; for(i = 1; i <= N; i ++) h[i] += h[i - 1]; for(i = SA::node; i >= 1; i --) q[-- h[sa[i].len]] = i; for(i = SA::node - 1; i >= 0; i --) { x = q[i]; for(j = 0; j < 26; j ++) f[x] += f[sa[x].next[j]]; ++ f[x]; } for(i = 0; i <= SA::node; i ++) for(j = k = 0; j < 26; j ++) if(f[sa[i].next[j]]) next[i][k] = sa[i].next[j], num[i][k] = f[sa[i].next[j]], ch[i][k] = j + 'a', ++ k; scanf("%d", &t); while(t --) { scanf("%d", &n); for(i = k = 0; i < 26; i ++) k += num[0][i]; n = (n - 1) % k + 1; rear = cur = 0; for(;;) { if(n == 0) break; for(i = 0; i < 26 && n > num[cur][i]; n -= num[cur][i ++]); -- n; b[rear ++] = ch[cur][i]; cur = next[cur][i]; } b[rear] = '\0'; puts(b); } } int main() { scanf("%s", b); init(); solve(); return 0; }