bzoj3998-弦论
给定一个长度为\(n(n\le 5\times 10^5)\)的字符串,求它的第\(k\)小字串。有两种模式:
- \(Type=0\),不同位置的相同字串只算一个
- \(Type=1\),不同位置相同字串算多个
Sample Input
aabc
0 3
Sample Output
aab
分析
我们知道,后缀自动机的从小到大遍历可以按顺序得到字符串的所有子串,所以要得到第\(k\)小的字串,只要用类似线段树上二分的做法,记录每一个出去的边后面有多少个即可。记\(val[i]\)为每个点的值,只要求一下\(sum[i]=val[i]+\sum _{trans(i,x)}sum[x]\)。对于两种不同的模式,我们对于\(val\)的设置不同。对于\(type=0\),我们把每个点的\(val\)始终为1,对于\(type=1\),初始时我们把\(val\)设为1,每次新加点的时候,把这个点的整条suffix-link上的点的\(val\)都加一,因为suffix-link代表的是后缀,形成了一个新的字符串后所有后缀的出现次数都增多了,由于一样的子串出现多次算多次,我们要给它算进去。最后dfs一下沿着路查找输出就好啦。很简单的题。
计算\(sum\)的时候,我用的方法是直接dfs一次,而这样会爆栈,所以我手工开栈了。还有一种方法,可以不需要dfs即可解决。我们知道,后缀自动机的\(trans\)组成了一个有向无环图,并且每个点代表的\(len\)与它们的拓扑序相符,而且\(len\)的大小不会超过\(n\),所以我们可以直接对\(len\)进行一次基数排序,相当于完成了拓扑排序,再按照拓扑倒序更新\(sum\)值。这种方法完全不需要递归,就不需要手工开栈了。复杂度同样是\(O(n)\)的。
代码
学一下手工开栈的方法。
更正:bzoj上提交不开栈也能过,因为系统是linux
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=5e5+1;
const int maxc=26;
char s[maxn];
int sta[maxn<<2],top=0;
bool alr[maxn<<1];
struct SAM {
int t[maxn<<1][maxc],len[maxn<<1],link[maxn<<1],val[maxn<<1],sum[maxn<<1],last,tot;
SAM ():last(1),tot(1) {}
void add(int x,int T) {
int nw=++tot,i;
len[nw]=len[last]+1;
val[nw]=1;
for (i=last;i && !t[i][x];i=link[i]) t[i][x]=nw;
if (i) {
int p=t[i][x];
if (len[p]==len[i]+1) link[nw]=p; else {
int q=++tot;
val[q]=val[p];
len[q]=len[i]+1;
memcpy(t[q],t[p],sizeof t[p]);
for (int j=i;j && t[j][x]==p;j=link[j]) t[j][x]=q;
link[q]=link[p];
link[p]=link[nw]=q;
}
} else link[nw]=1;
if (T) for (int i=link[nw];i;i=link[i]) ++val[i];
last=nw;
}
void dfs(int x) {
sum[x]=val[x];
for (int i=0;i<maxc;++i) if (t[x][i]) {
int v=t[x][i];
if (!alr[v]) {
alr[v]=true;
dfs(v);
}
sum[x]+=sum[v];
}
}
bool run(int x,int k) {
if (!k) return false;
for (int i=0;i<maxc;++i) if (t[x][i]) {
int v=t[x][i];
if (k>sum[v]) k-=sum[v]; else {
putchar(i+'a');
return run(v,k-val[v]);
}
}
return true;
}
} sam;
int main() {
int size=128<<20;
char *p=size+(char*)malloc(size);
__asm__("movl %0, %%esp\n" :: "r"(p));
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("my.out","w",stdout);
#endif
scanf("%s",s+1);
int n=strlen(s+1);
int T=read(),k=read();
for (int i=1;i<=n;++i) sam.add(s[i]-'a',T);
sam.dfs(1);
int ret=sam.run(1,k);
if (ret) puts("-1"); else puts("");
return 0;
}
bzoj3998-弦论的更多相关文章
- BZOJ3998 弦论 【SAM】k小子串
BZOJ3998 弦论 给一个字符串,问其第\(K\)小字串是什么 两种形式 1.不同起始位置的相同串只算一次 2.不同起始位置的相同串各算一次 首先建\(SAM\) 所有串的数量就是\(SAM\)中 ...
- 【BZOJ3998】弦论(后缀自动机)
[BZOJ3998]弦论(后缀自动机) 题面 BZOJ 题解 这题应该很简单 构建出\(SAM\)后 求出每个点往后还能构建出几个串 按照拓扑序\(dp\)一些就好了 然后就是第\(k\)大,随便搞一 ...
- 【BZOJ3998】[TJOI2015]弦论 后缀自动机
[BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...
- [bzoj3998][TJOI2015]弦论_后缀自动机
弦论 bzoj-3998 TJOI-2015 题目大意:给定一个字符串,求其$k$小子串. 注释:$1\le length \le 5\cdot 10^5$,$1\le k\le 10^9$. 想法: ...
- 【BZOJ-3998】弦论 后缀自动机
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2018 Solved: 662[Submit][Status] ...
- 【bzoj3998】 TJOI2015—弦论
http://www.lydsy.com/JudgeOnline/problem.php?id=3998 (题目链接) 题意 给出一个字符串,求它的字典序第K小的子串是什么,分情况讨论不在同一位置的相 ...
- BZOJ3998 TJOI2015弦论(后缀数组+二分答案)
先看t=1的情况.显然得求出SA(因为我不会SAM).我们一位位地确定答案.设填到了第len位,二分这一位填什么之后,在已经确定的答案所在的范围(SA上的某段区间)内二分,找到最后一个小于当前串的后缀 ...
- 【BZOJ3998】弦论 [SAM]
弦论 Time Limit: 10 Sec Memory Limit: 256 MB[Submit][Status][Discuss] Description 对于一个给定长度为N的字符串,求它的第 ...
- bzoj3998: [TJOI2015]弦论(SAM+dfs)
3998: [TJOI2015]弦论 题目:传送门 题解: SAM的入门题目(很好的复习了SAM并加强Right集合的使用) 其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接 ...
- bzoj3998: [TJOI2015]弦论
SAM小裸题qwq #include <iostream> #include <cstdio> #include <cmath> #include <cstr ...
随机推荐
- 20145234黄斐《Java程序设计》第九周学习总结
教材学习内容总结 JDBC Java语言访问数据库的一种规范,是一套API.JDBC (Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语 ...
- VS2005源代码视图出现了小蓝点,怎么弄掉?
VS2005源代码视图出现了小蓝点,怎么弄掉? 编辑->高级->查看空白行 就OK啦~ 这个查看空格的.. 或者Ctrl+E,S
- 5、Java并发编程:Lock
Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.l ...
- spring源码-事件&监听3.6
一.spring中的发布与监听模式,是我们最常用的一种观察者模式.spring在其中做了很多优化,目的就是让用户更好的使用事件与监听的过程. 二.常用的事件与监听中涉及到的接口和类为:Applicat ...
- connect by 语句
create table tb_menu( id number(10) not null, --主键id titlevarchar2(50), --标题 parent number(10) --par ...
- Spring学习记录-Java 11运行eureka-server报javax.xml.bind.JAXBContext not present错
在pom.xml加入依赖就行 <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId&g ...
- hdu2509Be the Winner(反nim博弈)
Be the Winner Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- jmeter基础之录制篇
一.前言 jmeter如今被越来越多人喜爱的一款测试工具,相比于loadrunner它体积特轻便.jmeter不仅用来做单接口测试,压测还能做性能,主要是一款开源的,可以写一个你需要的插件功能再添加里 ...
- 查看linux系统版本命令汇总
Linux下如何查看版本信息, 包括位数.版本信息以及CPU内核信息.CPU具体型号等等,整个CPU信息一目了然. 1.Linux查看版本当前操作系统内核信息 命令:uname -a
- 爬虫1.5-ajax数据爬取
目录 爬虫-ajax数据爬取 1. ajax数据 2. selenium+chromedriver知识准备 3. selenium+chromedriver实战拉勾网爬虫代码 爬虫-ajax数据爬取 ...