题解-TJOI2015 弦论
字符串 \(s\) 和 \(t\) 和 \(k\)。如果 \(t=0\),不同位置的相同子串算 \(1\) 个;如果 \(t=1\),不同位置的相同子串算多个。求 \(k\) 小子串,如果不存在输出 \(-1\)。
数据范围:\(1\le n\le 5\cdot 10^5\),\(t\in\{0,1\}\),\(1\le k\le 10^9\)。
这题还是很经典的,对理解后缀自动机 \(\tt SAM\) 很有帮助。以前我做过这题(并写了题解),现在复习后缀自动机的时候又做了一次,感悟颇多,遂记之。
首先后缀自动机的节点表示的是一个 \(\bf Endpos\) 集以及该集对应的子串(不一定是后缀)。
一个节点 \(i\) 对应的子串长度范围为 \([len_{fa_i}+1,len_i]\),即对应子串种数为 \(len_i-len_{fa_i}\)。
同时对应每种子串的数量均为 \(|{\bf Endpos}_i|\) 个。
先看处理这些种数、数量等奇奇怪怪的东西的代码(\(dep\) 即 \(len\)):
void run(int t){
for(int i=1;i<=cnt;i++) c[dep[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=1;i<=cnt;i++) q[c[dep[i]]--]=i;
for(int i=cnt;i>=1;i--) sz[fa[q[i]]]+=sz[q[i]]; //①
for(int i=1;i<=cnt;i++) sm[i]=t?sz[i]:(sz[i]=1); //②
sz[1]=sm[1]=0;
for(int i=cnt;i>=1;i--)
for(int c=0;c<26;c++) sm[q[i]]+=sm[ch[q[i]][c]]; //③
}
这个 \(q\) 数组是对后缀自动机节点按 \(len\) 排序(\(len_i>len_{fa_i}\))。
①:求出 \(sz_i=|{\bf Endpos}_i|\)。
②:按照题目要求处理。
③:处理子自动机子串数量和 \(sm_i\),一个 \(|{\bf Endpos}_i|\) 被算 \(len_i-len_{fa_i}\) 次。
至于输出 \(k\) 大子串,一个 \(\tt Dfs\) 的问题。
void Print(int p,int k){
if(k<=sz[p]) return;
k-=sz[p];
for(int c=0;c<26;c++)if(ch[p][c]){
if(k>sm[ch[p][c]]) k-=sm[ch[p][c]];
else return void((putchar(c+'a'),Print(ch[p][c],k)));
}
}
- 代码
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//Data
const int N=5e5;
int n;
char s[N+7];
//SuffuxAutomaton
const int T=N<<1;
int en=1,cnt=1,ch[T+7][26],fa[T+7],dep[T+7]; //dep即len
ll sz[T+7],sm[T+7];
void insert(int c){
int p=en,np=en=++cnt;
dep[np]=dep[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else {
int q=ch[p][c];
if(dep[q]==dep[p]+1) fa[np]=q;
else {
int nq=++cnt;
dep[nq]=dep[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
sz[np]=1;
}
int c[T+7],q[T+7];
void run(int t){
for(int i=1;i<=cnt;i++) c[dep[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=1;i<=cnt;i++) q[c[dep[i]]--]=i;
for(int i=cnt;i>=1;i--) sz[fa[q[i]]]+=sz[q[i]];
for(int i=1;i<=cnt;i++) sm[i]=t?sz[i]:(sz[i]=1);
sz[1]=sm[1]=0;
for(int i=cnt;i>=1;i--)
for(int c=0;c<26;c++) sm[q[i]]+=sm[ch[q[i]][c]];
}
void Print(int p,int k){
if(k<=sz[p]) return;
k-=sz[p];
for(int c=0;c<26;c++)if(ch[p][c]){
if(k>sm[ch[p][c]]) k-=sm[ch[p][c]];
else return void((putchar(c+'a'),Print(ch[p][c],k)));
}
}
//Main
int main(){
int t,k; scanf("%s%d%d",&s[1],&t,&k),n=strlen(&s[1]);
for(int i=1;i<=n;i++) insert(s[i]-'a');
run(t);
if(sm[1]>=k) Print(1,k); else puts("-1");
return 0;
}
祝大家学习愉快!
题解-TJOI2015 弦论的更多相关文章
- 【BZOJ3998】[TJOI2015]弦论 后缀自动机
[BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...
- bzoj3998: [TJOI2015]弦论(SAM+dfs)
3998: [TJOI2015]弦论 题目:传送门 题解: SAM的入门题目(很好的复习了SAM并加强Right集合的使用) 其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接 ...
- BZOJ 3998: [TJOI2015]弦论 [后缀自动机 DP]
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2152 Solved: 716[Submit][Status] ...
- Luogu P3975 [TJOI2015]弦论
题目链接 \(Click\) \(Here\) 题目大意: 重复子串不算的第\(k\)大子串 重复子串计入的第\(k\)大子串 写法:后缀自动机. 和\(OI\) \(Wiki\)上介绍的写法不太一样 ...
- 洛谷 P3975 [TJOI2015]弦论 解题报告
P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...
- 【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2627 Solved: 881 Description 对于一 ...
- BZOJ_3998_[TJOI2015]弦论_后缀自动机
BZOJ_3998_[TJOI2015]弦论_后缀自动机 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行 ...
- luogu P3975 [TJOI2015]弦论 SAM
luogu P3975 [TJOI2015]弦论 链接 bzoj 思路 建出sam. 子串算多个的,统计preant tree的子树大小,否则就是大小为1 然后再统计sam的节点能走到多少串. 然后就 ...
- LGOJ3975 TJOI2015 弦论
link:TJOI2015 弦论 题目大意: 给定一个字符串,输出在对该字符串所有的非空子串排序后第\(k\)个 另外的一个限制是\(T\):子串本质相同但位置不同算\(1\)或多个 \(|s| \l ...
随机推荐
- wait函数与waitpid函数(僵尸进程)
当子进程退出时,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程.它只保留最小的一些 ...
- 【python爬虫】用requests库模拟登陆人人网
说明:以前是selenium登陆取cookie的方法比较复杂,改用这个 """ 用requests库模拟登陆人人网 """ import r ...
- OMV openmediavault NAS系统命令显示颜色
闲鱼65f元买的我家云刷了OMV系统. 但ls命令查看文件不显示颜色. cd /etc/进入配置文件目录查看并没有bashrc文件,但有个bash.bashrc 在 bash.bashrc后面加入以下 ...
- mysql 不常用备忘
# group_concat 函数语法: group_concat( [DISTINCT] 要连接的字段 [Order BY 排序字段 ASC/DESC] [Separator '分隔符'] ...
- FL studio系列教程(十四):如何在FL Studio播放列表中排列样式
我们在FL Studio中做好了节奏样式后就可以在播放列表窗口中进行乐曲的编排了.刚接触这款软件的同学肯定会对如何编排比较陌生但也比较憧憬的,因为它是从一个窗口到另一个窗口中的操作.其实明白了这里的知 ...
- ClassLoader分类
对于类装载器而言一共有三种, 1分别是加载rt包下的Bootstrap加载器,是用C++写的,是在java最早发布的时候写的,用于加载那些最初的类. 2然后java在发展过程中又要发布新的jdk,所以 ...
- linux配置tomcat项目包
安装配置tomcat 操作系统: centos 7.1 使用模式:命令模式 使用工具:xshell5 .xftp5 注意之前有配置过tomcat需要进行以下4个步骤 1.关闭网管服务 输入命令: 进 ...
- jmeter多用户登录并发测试
在使用Jmeter进行性能测试时,我们通常会需要配置多个不同用户进行并发测试,这里简单介绍一下配置方法. 1.运行Jmeter.bat, 在打开的测试计划中右键添加一个线程组: 2.在线程组下添加录 ...
- Linux初学学习笔记 -----正则表达式和通配符
简单来说通配符是用来匹配文件名和目录而正则表达式是用来匹配文本内容的 常用的通配符 *:匹配任意多个字符 下面的是以p为开头的目录里面的文件 ?:匹配任意一个字符 [-]:匹配括号内出现的任意一个字符 ...
- 不使用 MQ 如何实现 pub/sub 场景?
hello,大家好,我是小黑,又和大家见面啦~~ 在配置中心中,有一个经典的 pub/sub 场景:某个配置项发生变更之后,需要实时的同步到各个服务端节点,同时推送给客户端集群. 在之前实现的简易版配 ...