您的位置:首页 > 其它

[BZOJ3998][TJOI2015]弦论-后缀树

2017-12-11 23:23 323 查看

弦论

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,则将当前k减去sum[i],并继续遍历下一个儿子。

否则,判断当前儿子父亲边的cnt[i]是否大于等于当前的k。

如果是,那么答案子串的结尾显然就在当前边上,找到这个位置并输出。

否则,输出当前边所代表的字符串并递归该儿子。

当T=1时,每条边的cnt[i]要乘以其子树内的叶子结点个数,因为每个当前边子树内的后缀均对当前边有1的贡献,而这样的后缀数量正是叶子节点的数量。T=0时则不用。

那么这样就做完了~

如果觉得这份代码很眼熟,那么你上一次看到的代码可能跟这次的代码作者相同

#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
;
ll ans
,cnt
,sum
;
char str
;

namespace suffix_tree
{
const int M=27;
const int Inf=1e9+7;

char s
;
int root,pos,last;
int actpos,actedge,actlen,remain;
int l
,r
,fail
,ch
[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: