P3975 [TJOI2015]弦论
思路
一眼SAM板子,结果敲了一中午。。。
我还是太弱了
题目要求求第k小的子串
我们可以把t=0当成t=1的特殊情况,(所有不同位置的相同子串算作一个就是相当于把所有子串的出现位置个数(endpos大小)全部赋成1)
然后讨论如何递推的求第k小的子串
首先要统计一个sum值,代表从这个状态出发能够到达多少个子串,相当于这个节点后继节点的所有值的和,反向拓扑一下能到那些节点即可
然后逐个dfs每个位置的字符是什么即可
注意
反向拓扑不一定要真的反向建边(狗头),我这么乱搞了。。。
可以把每个点的maxlen从小到大基数排序一发,然后正向拓扑就是从小到大,反向就是从大到小
会快很多
因为乱搞只能开O2过的代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define int long long
const int MAXN = 500100*2;
using namespace std;
int trans[MAXN][26],suflink[MAXN],endpos[MAXN],maxlen[MAXN],minlen[MAXN],Nodecnt,ispre[MAXN],in[MAXN],sum[MAXN];
char s[MAXN],ans[MAXN];
int new_state(int _maxlen,int _minlen,int *_trans,int _suflink){
++Nodecnt;
maxlen[Nodecnt]=_maxlen;
minlen[Nodecnt]=_minlen;
if(_trans)
for(int i=0;i<26;i++)
trans[Nodecnt][i]=_trans[i];
suflink[Nodecnt]=_suflink;
return Nodecnt;
}
int add_len(int u,int c){
int z=new_state(maxlen[u]+1,0,NULL,0);
ispre[z]=1;
while(u&&trans[u][c]==0){
trans[u][c]=z;
u=suflink[u];
}
if(!u){
suflink[z]=1;
minlen[z]=1;
return z;
}
int v=trans[u][c];
if(maxlen[v]==maxlen[u]+1){
suflink[z]=v;
minlen[z]=maxlen[v]+1;
return z;
}
int y=new_state(maxlen[u]+1,0,trans[v],suflink[v]);
suflink[v]=suflink[z]=y;
minlen[v]=minlen[z]=maxlen[y]+1;
while(u&&trans[u][c]==v){
trans[u][c]=y;
u=suflink[u];
}
minlen[y]=maxlen[suflink[y]]+1;
return z;
}
queue<int> q;
void topu_size(int tx){
for(int i=1;i<=Nodecnt;i++)
in[suflink[i]]++;
for(int i=1;i<=Nodecnt;i++)
if(!in[i])
q.push(i);
while(!q.empty()){
int x=q.front();
q.pop();
endpos[x]+=ispre[x];
endpos[suflink[x]]+=endpos[x];
in[suflink[x]]--;
if(!in[suflink[x]])
q.push(suflink[x]);
}
}
int u[MAXN*10],v[MAXN*10],fir[MAXN*10],nxt[MAXN*10],cnt;
void addedge(int ui,int vi){
++cnt;
u[cnt]=ui;
v[cnt]=vi;
nxt[cnt]=fir[ui];
in[vi]++;
fir[ui]=cnt;
}
void topu_sum(int tx){
for(int i=1;i<=Nodecnt;i++){
for(int j=0;j<26;j++)
if(trans[i][j]){
addedge(trans[i][j],i);
}
if(tx==0){
endpos[i]=sum[i]=1;
}
else{
sum[i]=endpos[i];
}
}
sum[1]=endpos[1]=0;
for(int i=1;i<=Nodecnt;i++)
if(!in[i])
q.push(i);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=fir[x];i;i=nxt[i]){
sum[v[i]]+=sum[x];
in[v[i]]--;
if(!in[v[i]])
q.push(v[i]);
}
}
}
int k,t,n;
int dfs(int pos,int x,int tx){
// printf("pos=%lld x=%lld k=%lld\n",pos,x,k);
if(k<=endpos[x])
return 1;
k-=endpos[x];
for(int i=0;i<26;i++){
if(!trans[x][i])
continue;
// printf("gg %d\n",trans[x][i]);
int mid=sum[trans[x][i]];
if(k>mid)
k-=mid;
else{
// printf("to %d\n",trans[x][i]);
// k-=endpos[trans[x][i]];
ans[pos]='a'+i;
return dfs(pos+1,trans[x][i],tx);
}
}
return -1;
}
signed main(){
freopen("1.in","r",stdin);
scanf("%s",s+1);
scanf("%lld %lld",&t,&k);
n=strlen(s+1);
Nodecnt=1;
int pre=1;
for(int i=1;i<=n;i++)
pre=add_len(pre,s[i]-'a');
// printf("ok\n");
topu_size(t);
topu_sum(t);
// for(int i=1;i<=Nodecnt;i++)
// printf("maxlen=%lld minlen=%lld endpos=%lld suflink=%lld sum=%lld\n",maxlen[i],minlen[i],endpos[i],suflink[i],sum[i]);
int req=dfs(1,1,t);
if(req==-1){
printf("-1\n");
return 0;
}
printf("%s\n",ans+1);
return 0;
}
P3975 [TJOI2015]弦论的更多相关文章
- Luogu P3975 [TJOI2015]弦论
题目链接 \(Click\) \(Here\) 题目大意: 重复子串不算的第\(k\)大子串 重复子串计入的第\(k\)大子串 写法:后缀自动机. 和\(OI\) \(Wiki\)上介绍的写法不太一样 ...
- 洛谷 P3975 [TJOI2015]弦论 解题报告
P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...
- luogu P3975 [TJOI2015]弦论 SAM
luogu P3975 [TJOI2015]弦论 链接 bzoj 思路 建出sam. 子串算多个的,统计preant tree的子树大小,否则就是大小为1 然后再统计sam的节点能走到多少串. 然后就 ...
- [洛谷P3975][TJOI2015]弦论
题目大意:求一个字符串的第$k$大字串,$t$表示长得一样位置不同的字串是否算多个 题解:$SAM$,先求出每个位置可以到达多少个字串($Right$数组),然后在转移图上$DP$,若$t=1$,初始 ...
- 并不对劲的bzoj3998:loj2102:p3975:[TJOI2015]弦论
题目大意 对于一个给定的长度为n(\(n\leq5*10^5\))的字符串, 分别求出不同位置的相同子串算作一个.不同位置的相同子串算作多个时,它的第k(\(k\leq10^9\))小子串是什么 题解 ...
- BZOJ 3998: [TJOI2015]弦论 [后缀自动机 DP]
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2152 Solved: 716[Submit][Status] ...
- 【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2627 Solved: 881 Description 对于一 ...
- 【BZOJ3998】[TJOI2015]弦论 后缀自动机
[BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...
- BZOJ_3998_[TJOI2015]弦论_后缀自动机
BZOJ_3998_[TJOI2015]弦论_后缀自动机 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行 ...
随机推荐
- 删除(unfork)github中某个库(repository)
1.头像->your profile 2.点击某个想要删除的库,出现界面: 3.点击setting,鼠标滚动页面至末尾,出现页面: 4.点击delete this repository: OK!
- map 的用法
#include<iostream> #include<map> #include<string> #define s second #define f first ...
- E. Gerald and Giant Chess
E. Gerald and Giant Chess time limit per test 2 seconds memory limit per test 256 megabytes2015-09-0 ...
- Window对象属性
2018-11-28 12:21:20
- Palindrome Bo (预处理 + 区间DP)
先进行离散化,然后再预处理出所有位置的下一个元素,做好这一步对时间的优化非常重要. 剩下的就是一般的DP了.区间DP #include<bits/stdc++.h> using names ...
- python二 总结--函数-- 装饰器
装饰器是什么? 有什么用? 为什么要用? 真的有用吗? 1.装饰器: 装饰器: 定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能. 原则:1.不能修改被装饰的函数的源代码 ...
- Lua 判断表是否为空方法
[1]判断表为空的方法 目前为止,Lua语言中判断table表是否为空有三种方式: (1)#table,当table为数组时直接返回table表的长度. (2)当table是字典时,返回table的长 ...
- python将一个列表的元素随机打乱
java可以用Collections.shuffle(List)来实现,python怎么实现呢? python要利用random模块的shuffle方法 代码如下: import random x = ...
- javamail邮件Multipart支持同时发text和html混合消息,alternative纯文本与超文本共存
javamail邮件Multipart支持同时发text和html混合消息alternative纯文本与超文本共存 multipart/mixed:附件. multipart/related:内嵌资源 ...
- Caddy – 方便够用的 HTTPS server 新手教程
最近发现了一个 golang 开发的 HTTP server,叫做 Caddy,它配置起来十分简便,甚至可以 28 秒配置好一个支持 http2 的 server ,而且对各种 http 新特性都支持 ...