弦论
Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
Sample Output
aab
HINT
N<=5*10^5
T<2
K<=10^9
出于某种目的寻找后缀树的题目,却一无所获……
然后就随手找了一道后缀自动机的题……
然后就很轻松地切掉了???
思路:
考虑到每一个子串均可以被表示为一个后缀的前缀,那么,建出后缀树。
然后,发现每一条边都有等于其代表的字符串长度的个数的子串。
那么,统计出每一条边的字符串个数
cnt[i]
,以及每一个点子树内及其父亲边的字符串个数之和
sum[i]
。
然后进行一次dfs,每走到一个节点,遍历其所有儿子。
如果当前儿子的
sum[i]
小于当前的
k
,则将当前
否则,判断当前儿子父亲边的
cnt[i]
是否大于等于当前的
k
。
如果是,那么答案子串的结尾显然就在当前边上,找到这个位置并输出。
否则,输出当前边所代表的字符串并递归该儿子。
当
那么这样就做完了~
如果觉得这份代码很眼熟,那么你上一次看到的代码可能跟这次的代码作者相同
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000009;
const ll inf=1e18+7;
int n,t,k,siz[N];
ll ans[N],cnt[N],sum[N];
char str[N];
namespace suffix_tree
{
const int M=27;
const int Inf=1e9+7;
char s[N];
int root,pos,last;
int actpos,actedge,actlen,remain;
int l[N],r[N],fail[N],ch[N][M],tot;
inline int len(int x){return min(r[x],pos+1)-l[x];}
inline int newnode(int lbound,int rbound=Inf)
{
l[++tot]=lbound;r[tot]=rbound;
return tot;
}
inline void link(int x)
{
if(last)fail[last]=x;
last=x;
}
inline bool walk(int x)
{
if(actlen>=len(x))
{
actedge+=len(x);
actlen-=len(x);
actpos=x;
return true;
}
return false;
}
inline void init()
{
last=tot=remain=actedge=actlen=0;
pos=-1;root=actpos=newnode(-1,-1);
}
inline void extend(int c)
{
s[++pos]=c;
last=0;
remain++;
while(remain>0)
{
if(actlen==0)
actedge=pos;
if(ch[actpos][s[actedge]]==0)
{
ch[actpos][s[actedge]]=newnode(pos);
link(actpos);
}
else
{
int nxt=ch[actpos][s[actedge]];
if(walk(nxt))continue;
if(s[l[nxt]+actlen]==c)
{
actlen++;
link(actpos);
break;
}
int split=newnode(l[nxt],l[nxt]+actlen);
ch[actpos][s[actedge]]=split;
ch[split][c]=newnode(pos);
l[nxt]+=actlen;
ch[split][s[l[nxt]]]=nxt;
link(split);
}
remain--;
if(actpos==root && actlen>0)
{
actlen--;
actedge=pos-remain+1;
}
else
actpos=(fail[actpos]?fail[actpos]:root);
}
}
}
inline void dfs(int u)
{
using namespace suffix_tree;
siz[u]=0;
for(int i=0;i<M;i++)
if(ch[u][i])
{
dfs(ch[u][i]);
siz[u]+=siz[ch[u][i]];
sum[u]+=sum[ch[u][i]];
}
if(!siz[u])
{
siz[u]=1;
sum[u]=cnt[u]=len(u)-1;
}
else
sum[u]+=(cnt[u]=(t==1?siz[u]:1)*len(u));
}
inline void putstr(int st,int ed)
{
using namespace suffix_tree;
for(int i=st;i<ed;i++)
putchar(s[i]+'a');
}
inline void dfs2(int u,int res)
{
using namespace suffix_tree;
for(int i=0;i<M;i++)
if(ch[u][i])
{
if(res<=sum[ch[u][i]])
{
if(res>cnt[ch[u][i]])
{
putstr(l[ch[u][i]],r[ch[u][i]]);
dfs2(ch[u][i],res-cnt[ch[u][i]]);
return;
}
else
{
int block=(t==1?siz[ch[u][i]]:1);
putstr(l[ch[u][i]],l[ch[u][i]]+(res+block-1)/block);
return;
}
}
else
res-=sum[ch[u][i]];
}
}
int main()
{
scanf("%s",str+1);
scanf("%d%d",&t,&k);
n=strlen(str+1);
suffix_tree::init();
for(int i=1;i<=n;i++)
suffix_tree::extend(str[i]-'a');
suffix_tree::extend(26);
dfs(suffix_tree::root);
if(k>sum[suffix_tree::root])
puts("-1");
else
dfs2(suffix_tree::root,k);
return 0;
}