您的位置:首页 > 其它

bzoj 3998 [TJOI2015]弦论

2017-05-11 13:03 162 查看


3998: [TJOI2015]弦论

Time Limit: 10 Sec  Memory Limit:
256 MB
Submit: 2703  Solved: 912

[Submit][Status][Discuss]

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

Source

【分析】

又发现了SAM的好多神奇的性质啊...

又看了很长时间代码才稍微能懂一点点...Orz

【代码】

//bzoj 3998 [TJOI2015]弦论
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=1000005;
char s[mxn];
int n,m,T,K,len;
int p,q,np,nq,tot,root;
int b[mxn],t[mxn],rig[mxn],sum[mxn];
int pre[mxn],step[mxn],son[mxn][27];
inline void sam()
{
int i,j;
scanf("%s",s+1);
len=strlen(s+1);
root=np=tot=1;
fo(i,1,len)
{
int c=s[i]-'a'+1;
p=np;
step[np=(++tot)]=step[p]+1;
while(p && !son[p][c])
son[p][c]=np,p=pre[p];
if(!p)
{
pre[np]=root;
continue;
}
q=son[p][c];
if(step[q]==step[p]+1)
pre[np]=q;
else
{
step[nq=(++tot)]=step[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(p && son[p][c]==q)
son[p][c]=nq,p=pre[p];
}
}
}
inline void basesort()
{
int i,j;
fo(i,1,len)
{
int c=s[i]-'a'+1;
p=son[p][c],rig[p]++;
}
fo(i,1,tot) b[step[i]]++;
fo(i,1,len) b[i]+=b[i-1];
fo(i,1,tot) t[b[step[i]]--]=i;
if(T) for(i=tot;i;i--) rig[pre[t[i]]]+=rig[t[i]];
else fo(i,1,tot) rig[i]=1;
rig[1]=0;
for(i=tot;i;i--)
{
p=t[i];sum[p]=rig[p];
fo(j,1,26) sum[p]+=sum[son[p][j]];
}
}
inline void dfs(int x,int K)
{
if(K<=rig[x]) return;
K-=rig[x];
for(int j=1;j<=26;j++)
{
p=son[x][j];
if(p)
{
if(K<=sum[p])
{
printf("%c",j+'a'-1);
dfs(p,K);
return;
}
K-=sum[p];
}
}
}
int main()
{
int i,j;
sam();
scanf("%d%d",&T,&K);
p=root;
basesort();
if(K>sum[1]) printf("-1\n");
else dfs(1,K);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: