解题思路:不重复的计算子串很简单,只要记忆化搜索,dp过去求每个节点后面有多少个串,然后用逼近法,去求出那个串就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
char str[mx],Ts[mx];
struct state
{
int fa,len;//fa为包含该转态的最相近节点,len是从根节点走到该转态的最长长度
int id[26];
}Tr[mx];
int tot,last,ans,v[mx],pos[mx],cnt[mx];
int n,m;
//后缀自动机定义,从根节点出发到达任意一个节点形成的串都是唯一的,且一定是母串的子串
void suf_auto(int ch)
{
int np = ++tot,p = last;
//其实last就是原来的一条直串扩展到的前一个位置
//比如abccbdab这个串,自动机上肯定存在最长的一个路径就是这个串,那么last就是一直在这上面的
Tr[np].len = Tr[last].len + 1;
last = np;
//for(;p&&!Tr[p].id[ch];p=Tr[p].fa) Tr[p].id[ch] = np;
while(p&&!Tr[p].id[ch]) Tr[p].id[ch] = np, p = Tr[p].fa;
//找到包含p转态的节点,那么它也可以转移到新转态np
//可以看出是短串包含长串
if(!p) Tr[np].fa = 1;//如果都没有包含p状态的节点有可以扩展出ch这个转态,那么新状态被根节点包含
else{
int q = Tr[p].id[ch];
//如果if成立,不存在另一个点t使得Tr[t].len + 1==Tr[q].len证明也很简单
if(Tr[p].len+1==Tr[q].len) Tr[np].fa = q;//说明q状态可以包含新状态np
else{
int nq = ++tot;
Tr[nq] = Tr[q];//分裂出q转态,就是分裂出if判断那个节点
Tr[nq].len = Tr[p].len + 1;
Tr[q].fa = Tr[np].fa = nq;//分裂出的nq肯定可以包含原来的点q和np
while(p&&Tr[p].id[ch]==q) Tr[p].id[ch] = nq,p = Tr[p].fa;
//将后面包含p的点的儿子是q的也变成分裂的那个,因为他们就是跟着p走的,分裂出来的nq自然是p直接扩展状态
}
}
}
void init()//初始化根节点为1号
{
last = tot = 1;
Tr[1].len = Tr[1].fa = 0;
//memset(Tr[1].id,0,sizeof(Tr[1].id));
//这里注意多个数据id数组要初始化
}
void dfs(int x)
{
cnt[x] = 1;
for(int i=0;i<26;i++){
if(!Tr[x].id[i]) continue;
if(!cnt[Tr[x].id[i]]) dfs(Tr[x].id[i]);
cnt[x] += cnt[Tr[x].id[i]];
}
}
void find(int x)
{
int ret=0,p = 1;
while(x){
for(int i=0;i<26;i++){
if(x>cnt[Tr[p].id[i]]) x -= cnt[Tr[p].id[i]];
else{
putchar(i+'a');
x--,p = Tr[p].id[i];
break;
}
}
}
}
int main()
{
init();
scanf("%s",str);
int len = strlen(str);
for(int i=0;i<len;i++) suf_auto(str[i]-'a');
dfs(1);
scanf("%d",&m);
while(m--){
scanf("%d",&n);
find(n);
puts("");
}
return 0;
}