标签:parent find 后缀自动机 int www. c++ utc mes using
对于一个给定长度为\(N\)的字符串,求它的第\(K\)小子串是什么。
第一行是一个仅由小写英文字母构成的字符串\(S\)
第二行为两个整数\(T\)和\(K\),\(T\)为0则表示不同位置的相同子串算作一个。\(T=1\)则表示不同位置的相同子串算作多个。\(K\)的意义如题所述。
输出仅一行,为一个数字串,为第\(K\)小的子串。如果子串数目不足\(K\)个,则输出\(-1\)
aabc
0 3
aab
\(N<=5*10^5\)
\(T<2\)
\(K<=1e9\)
中文题面,不解释。
把串放进后缀自动机,然后处理一遍,如果\(T=0\),则所有点权为1;否则,把每个点的\(parent\)加上当前\(size\)。然后反向拓扑,像求第\(k\)大子串如这个一样求就行了。
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char s[N];
int a[N],c[N];
void cmax(int &a,int b){
a=max(a,b);
}
void cmin(int &a,int b){
a=min(a,b);
}
struct SAM{
int last,cnt;
int size[N],ch[N][26],fa[N<<1],l[N<<1],sum[N];
void ins(int c){
int p=last,np=++cnt;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[p]+1==l[q])fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
size[np]=1;
}
void build(char s[]){
int len=strlen(s+1);
last=cnt=1;
for(int i=1;i<=len;++i)ins(s[i]-‘a‘);
}
void calc(int op){
memset(c,0,sizeof c);
for(int i=1;i<=cnt;++i)c[l[i]]++;
for(int i=1;i<=cnt;++i)c[i]+=c[i-1];
for(int i=1;i<=cnt;++i)a[c[l[i]]--]=i;
for(int i=cnt;i;--i){
int p=a[i],f=fa[p];
if(op){
size[f]+=size[p];
}else{
size[p]=1;
}
}
size[1]=0;
for(int i=cnt;i;--i){
int p=a[i];
sum[p]=size[p];
for(int j=0;j<26;++j){
if(ch[p][j])sum[p]+=sum[ch[p][j]];
}
}
}
void find(int k){
int p=1;
size[0]=0;
while(k){
int a=0;
while(k>sum[ch[p][a]]&&a<26){
if (ch[p][a]) k-=sum[ch[p][a]];
a++;
}
if(a>=26){
puts("-1");
return;
}
putchar(‘a‘+a);k-=size[ch[p][a]];
if(k<=0)return;
p=ch[p][a];
}
}
}sam;
int main(){
cin>>s+1;
sam.build(s);
int t,k;
cin>>t>>k;
sam.calc(t);
sam.find(k);
}
标签:parent find 后缀自动机 int www. c++ utc mes using
原文地址:https://www.cnblogs.com/A1269180380/p/10098710.html