题目链接、
题意:
给你一个字符串,问你在算重复/不算重复的情况下第k大的字符串是什么,不存在输出-1。n<=5e5。
题解:
如果不算重复的话和这个题一样。看链接里的题解就行,我不再重复了。
那么我们就讲一下算重复的做法。算重复其实就是要通过right来计算相同的情况,计算的方法和洛谷模板SAM的做法类似。都是在parent树上dfs,然后用子节点size更新父节点,其他的与可不算重复的一样做了。
代码:
#include <bits/stdc++.h>
using namespace std;
int opt,n,k,fa[2000010],cnt=1,rt=1,lst=1,ch[2000010][26],len[2000010];
int sz[2000010],ri[2000010],a[2000010],rk[2000010];
char s[500010];
inline void insert(int x)
{
int cur=++cnt,pre=lst;
ri[cur]=1;
lst=cur;
len[cur]=len[pre]+1;
for(;pre&&!ch[pre][x];pre=fa[pre])
ch[pre][x]=cur;
if(!pre)
fa[cur]=rt;
else
{
int ji=ch[pre][x];
if(len[ji]==len[pre]+1)
fa[cur]=ji;
else
{
int gg=++cnt;
memcpy(ch[gg],ch[ji],sizeof(ch[ji]));
len[gg]=len[pre]+1;
fa[gg]=fa[ji];
fa[ji]=fa[cur]=gg;
for(;pre&&ch[pre][x]==ji;pre=fa[pre])
ch[pre][x]=gg;
}
}
}
inline void query(int k)
{
if(sz[rt]<k)
{
printf("-1\n");
return;
}
int ji=rt;
while(k)
{
if(ji!=rt)
k-=ri[ji];
if(k<=0)
break;
for(int i=0;i<=25;++i)
{
if(ch[ji][i])
{
if(sz[ch[ji][i]]<k)
k-=sz[ch[ji][i]];
else
{
printf("%c",'a'+i);
ji=ch[ji][i];
break;
}
}
}
}
printf("\n");
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
scanf("%d%d",&opt,&k);
for(int i=1;i<=n;++i)
insert(s[i]-'a');
for(int i=1;i<=cnt;++i)
a[len[i]]++;
for(int i=1;i<=n;++i)
a[i]+=a[i-1];
for(int i=cnt;i>=1;--i)
rk[a[len[i]]--]=i;
if(opt==0)
{
for(int i=cnt;i>=1;--i)
ri[rk[i]]=1;
}
else
{
for(int i=cnt;i>=1;--i)
ri[fa[rk[i]]]+=ri[rk[i]];
}
ri[1]=0;
for(int i=cnt;i>=1;--i)
{
sz[rk[i]]=ri[rk[i]];
for(int j=0;j<=25;++j)
sz[rk[i]]+=sz[ch[rk[i]][j]];
}
query(k);
return 0;
}