题意:
给定一个长度为 n 的字符串,让求出它的第 $k$ 小子串是什么。每次除字符串之外还会给定两个参数 $t $和 $k$ ,$t$ 为 $0$ 则表示不同位置的相同子串算作一个, $t$ 为 $1$ 则表示不同位置的相同子串算作多个。数据范围 $n\le 5*10^5$ $t\in {0,1}$ , $k \le 10^9$.
题解:
后缀自动机板子题。我们知道每一个子串放在sam上跑一定可以走到一个合法节点,反过来说任意一个节点一定代表了一批能走到这儿的子串。那么我们可以想办法把到达每个节点有多少种子串用dag图上dp的方式求出来,然后在t=1的时候,根据sam的性质,给每个dp值再乘以一个 $|endpos|$ 就可以了。学习到的是比我原来板子更快的拓扑排序方式。。。直接用len结果在数组里排序,比起用queue来常数小了很多。另外还纠正了自己的对于sam拓扑序的一个错误认识,他其实无论是slink还是dwag的拓扑序都是一样的,因为无论哪种都一定是从小的子串往大的走。
wa点:
- 拓扑排序之后Sort的内容是从后往前的,注意枚举的时候要反过来。
- 是否是前缀的标记,原来的写法注释是错的,更新了板子。
#include<bits/stdc++.h>
#define CHAR 26
#define nullptr 0
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
const int debug=0;
struct sam_node{
sam_node* fa,*nxt[CHAR];//fa is slink...
int len;//max len
bool flag;
void clear(){
fa=nullptr;
flag=0;
memset(nxt,0,sizeof(nxt));
}
int hmn(){return len-fa->len;}
}pool[maxn<<1];
sam_node *root,*tail;
sam_node* newnode(int len){
sam_node* cur=tail++;
cur->clear();
cur->len=len;
return cur;
}
void sam_init(){
tail=pool;
root=newnode(0);
}
sam_node * extend(sam_node* last,int x){
sam_node* p=last,*np=newnode(p->len+1);
while(p&&p->nxt[x]==nullptr)
p->nxt[x]=np,p=p->fa;
if(p==nullptr)
np->fa=root;
else{
sam_node* q=p->nxt[x];
if(q->len==p->len+1)
np->fa=q;
else{
sam_node* nq=newnode(p->len+1);
memcpy(nq->nxt,q->nxt,sizeof(q->nxt));
nq->fa=q->fa;
q->fa=np->fa=nq;
while(p!=nullptr&&p->nxt[x]==q)
p->nxt[x]=nq,p=p->fa;
}
}
np->flag=1;
return np;
}
int Len,ssz;
char ori[maxn];
sam_node* End[maxn];
void build(){
Len=strlen(ori);
sam_init();
sam_node* pre=root,*cr;
End[0]=root;
for(int i=1;i<=Len;++i) End[i]=extend(End[i-1],ori[i-1]-'a');
ssz=tail-pool;
//debug
// if(debug){
// cout<<"Debug build\n";
// sam_node *cur=root;
// cout<<ssz<<endl;
// for(int i=0;i<ssz;++i){
// for(int j=0;j<CHAR;++j){
// if((cur+i)->nxt[j]!=nullptr)
// cout<<i<<" "<<(char)(j+'a')<<" "<<((cur+i)->nxt[j]-pool)<<endl;
// }
// }
// }
}
//Topsort from ssz to 0 ,both for slink and dwag
int topsort[maxn<<1],ctp[maxn];
void Topsort(){
for(int i=0;i<=Len;++i) ctp[i]=0;
for(int i=0;i<ssz;++i) ctp[pool[i].len]++;
for(int i=1;i<=Len;++i) ctp[i]+=ctp[i-1];//?
for(int i=ssz-1;i>=0;i--) topsort[--ctp[pool[i].len]]=i;
}
//Get |endpos|
int endpos[maxn<<1];
void getEndpos(int op){
if(op==0){
for(int i=0;i<ssz;++i) endpos[i]=1;
return ;
}
sam_node* cur;
for(int i=ssz-1;i>=0;--i){
cur=pool+topsort[i];
if(cur->flag)
endpos[topsort[i]]++;
//slink
if(cur->fa!=nullptr)
endpos[cur->fa-pool]+=endpos[topsort[i]];
}
//debug
// if(debug){
// cout<<"Debug endpos\n";
// for(int i=0;i<ssz;++i){
// cout<<i<<" "<<endpos[i]<<endl;
// }
// }
}
int t,k;
ll dp[maxn<<1];
void solve(){
sam_node *cur,*son;
Topsort();
getEndpos(t);
for(int i=ssz-1;i>=0;--i){
cur=pool+topsort[i];
for(int j=0;j<CHAR;++j){
if(cur->nxt[j]==nullptr) continue;
dp[cur-pool]+=(endpos[cur->nxt[j]-pool]+dp[cur->nxt[j]-pool]);
}
}
// if(debug){
// for(int i=0;i<ssz;++i){
// cout<<i<<" "<<dp[i]<<endl;
// }
// }
cur=root;
while(k>0){
int i;
for(i=0;i<CHAR &&k>0;++i){
son=cur->nxt[i];
if(son==nullptr) continue;
if(k<=dp[son-pool]+(endpos[son-pool])){
//putchar('a'+i);
printf("%c",i+'a');
k-=endpos[son-pool];
cur=son;
break;
}
else{
k-=dp[son-pool]+(endpos[son-pool]);//error2
}
}
if(i==CHAR){
puts("-1");
break;
}
}
}
int main(){
scanf(" %s %d %d",ori,&t,&k);
build();
solve();
return 0;
}
/*
aabbabd
1 2
debug build output should be
10
0 a 1
0 b 5
0 d 9
1 a 2
1 b 8
2 b 3
3 b 4
4 a 6
5 a 6
5 b 4
5 d 9
6 b 7
7 d 9
8 b 4
8 d 9
*/
