[BZOJ3998][TJOI2015]弦论-后缀树
2017-12-11 23:23
323 查看
弦论
Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。Input
第一行是一个仅由小写英文字母构成的字符串S第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1Sample Input
aabc0 3
Sample Output
aabHINT
N<=5*10^5T<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; }
相关文章推荐
- bzoj3998 [TJOI2015]弦论(SAM)
- [BZOJ3998]-[TJOI2015]弦论-后缀自动机
- BZOJ 3998: [TJOI2015]弦论
- [BZOJ3998]TJOI2015弦论|后缀自动机
- bzoj3998 && [TJOI2015]弦论
- bzoj 3998 [TJOI2015]弦论
- BZOJ 3998 TJOI 2015 弦论 后缀自动机
- BZOJ 3998 [TJOI2015]弦论
- BZOJ3998 TJOI2015弦论(后缀数组+二分答案)
- [BZOJ3998][TJOI2015]弦论 后缀自动机
- 【bzoj3998】[TJOI2015]弦论
- BZOJ3998 [TJOI2015]弦论 【后缀自动机】
- BZOJ_3998_[TJOI2015]弦论_后缀自动机
- bzoj 3998 [TJOI2015]弦论
- bzoj3998[TJOI2015]弦论
- 【后缀自动机】 BZOJ 3998: [TJOI2015]弦论
- bzoj 3998: [TJOI2015]弦论
- 【BZOJ】3998: [TJOI2015]弦论
- [BZOJ3998][TJOI2015]弦论
- BZOJ 3998 [TJOI2015]弦论【后缀自动机(总结+安利