思路

一眼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]弦论的更多相关文章

  1. Luogu P3975 [TJOI2015]弦论

    题目链接 \(Click\) \(Here\) 题目大意: 重复子串不算的第\(k\)大子串 重复子串计入的第\(k\)大子串 写法:后缀自动机. 和\(OI\) \(Wiki\)上介绍的写法不太一样 ...

  2. 洛谷 P3975 [TJOI2015]弦论 解题报告

    P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...

  3. luogu P3975 [TJOI2015]弦论 SAM

    luogu P3975 [TJOI2015]弦论 链接 bzoj 思路 建出sam. 子串算多个的,统计preant tree的子树大小,否则就是大小为1 然后再统计sam的节点能走到多少串. 然后就 ...

  4. [洛谷P3975][TJOI2015]弦论

    题目大意:求一个字符串的第$k$大字串,$t$表示长得一样位置不同的字串是否算多个 题解:$SAM$,先求出每个位置可以到达多少个字串($Right$数组),然后在转移图上$DP$,若$t=1$,初始 ...

  5. 并不对劲的bzoj3998:loj2102:p3975:[TJOI2015]弦论

    题目大意 对于一个给定的长度为n(\(n\leq5*10^5\))的字符串, 分别求出不同位置的相同子串算作一个.不同位置的相同子串算作多个时,它的第k(\(k\leq10^9\))小子串是什么 题解 ...

  6. BZOJ 3998: [TJOI2015]弦论 [后缀自动机 DP]

    3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2152  Solved: 716[Submit][Status] ...

  7. 【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )

    3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2627  Solved: 881 Description 对于一 ...

  8. 【BZOJ3998】[TJOI2015]弦论 后缀自动机

    [BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...

  9. BZOJ_3998_[TJOI2015]弦论_后缀自动机

    BZOJ_3998_[TJOI2015]弦论_后缀自动机 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行 ...

随机推荐

  1. POJ1944 Fiber Communications (USACO 2002 February)

    Fiber Communications 总时间限制:  1000ms 内存限制:  65536kB 描述 Farmer John wants to connect his N (1 <= N ...

  2. hdu5067

    题意 给了一个n*m的网格 然后一台挖掘机从(0,0) 这个位置出发,收集完全部的石头回到(0,0)挖掘机可以有无限的载重 用旅行商处理 dp[k][i] 表示在这个集合中最后到达i的最小距离,用集合 ...

  3. javamail发送邮件及错误解决方法javax.mail.AuthenticationFailedException: failed to connect, no password specified?

    javamail发送邮件及错误解决方法javax.mail.AuthenticationFailedException: failed to connect, no password specifie ...

  4. windows下多个python版本共存,如何在Windows7系统上安装最新的64位Python3.6.2

    windows下多个python版本共存,如何在Windows7系统上安装最新的64位Python3.6.2 1.官网下载python3.6.2https://www.python.org/ftp/p ...

  5. vue路由6:导航钩子

    <div id="app"> <div> <router-link to="/">首页</router-link> ...

  6. java初学者必看的学习路线

    不管在编程语言的排行榜中,还是在大多数企业应用的广泛程度来看,Java一直都是当之无愧的榜首.Java语言有着独特的魅力吸引着广大的年轻人去学习,每个人学习的方式方法不一样. 第一步:首先要做好学习前 ...

  7. jsky使用小记

    jsky是一款深度WEB应用安全评估工具,能轻松应对各种复杂的WEB应用,全面深入发现里面存在的安全弱点. jsky可以检测出包括SQL注入.跨站脚本.目录泄露.网页木马等在内的所有的WEB应用层漏洞 ...

  8. Oracle初级第一天

    oracle卸载 运行regedit,删除HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ ...

  9. AirTest源码分析之运行器

    from: https://blog.csdn.net/u012897401/article/details/82900562 使用:根据airtest文档说明,可以通过命令行来启动air脚本,需要传 ...

  10. vertica导出导入数据

    直接开. 导出dat vsql -U dbadmin -wlizhenghua -At -F'|' -c "select * from APP_INTER_BOSS_CDR_COUNT_T& ...