洛谷3975 BZOJ3998 TJOI2015 弦論 字尾自動機
阿新 • • 發佈:2018-12-07
題目連結、
題意:
給你一個字串,問你在算重複/不算重複的情況下第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;
}