SPOJ SUBLEX
Problem : 给一个长度为n的字符串,要求输出字典序第k小的子串。
Solution :首先对字符串建立后缀自动机。从根节点开始根据nt数组即后缀链进行一边dfs记忆化搜索,预处理出当前状态下之后还能形成的字符串数量。对于每一个询问,从小到大枚举每一位的每个字母即可。
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 250008;
struct Suffix_Automaton
{
int nt[N << 1][26], a[N << 1], fail[N << 1];
int tot, last, root;
int p, q, np, nq;
int cnt[N << 1];
int newnode(int len)
{
for (int i = 0; i < 26; ++i) nt[tot][i] = -1;
fail[tot] = -1; a[tot] = len; cnt[tot] = 0;
return tot++;
}
void clear()
{
tot = last = 0;
root = newnode(0);
}
void insert(int ch, int k)
{
p = last; last = np = newnode(a[p] + 1);
for (; ~p && nt[p][ch] == -1; p = fail[p]) nt[p][ch] = np;
if (p == -1) fail[np] = root;
else
{
q = nt[p][ch];
if (a[p] + 1 == a[q]) fail[np] = q;
else
{
nq = newnode(a[p] + 1);
for (int i = 0; i < 26; ++i) nt[nq][i] = nt[q][i];
fail[nq] = fail[q];
fail[np] = fail[q] = nq;
for (; ~p && nt[p][ch] == q; p = fail[p]) nt[p][ch] = nq;
}
}
}
int dfs(int u)
{
if (cnt[u]) return cnt[u];
cnt[u] = 1;
for (int i = 0; i < 26; ++i)
if (~nt[u][i])
{
dfs(nt[u][i]);
cnt[u] += cnt[nt[u][i]];
}
}
void solve(int k)
{
for (int p = root; k > 0;)
{
for (int i = 0; i < 26; ++i)
if (~nt[p][i])
if (k <= cnt[nt[p][i]])
{
printf("%c",'a' + i);
k--;
p = nt[p][i];
break;
}
else k -= cnt[nt[p][i]];
}
printf("
");
}
}sam;
int main()
{
cin.sync_with_stdio(0);
sam.clear();
string s; cin >> s;
for (int i = 0, len = s.length(); i < len; ++i)
sam.insert(s[i] - 'a', i);
sam.dfs(sam.root);
int q; cin >> q;
for (int i = 1; i <= q; ++i)
{
int k; cin >> k;
sam.solve(k);
}
}