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

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

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

  2. bzoj3998: [TJOI2015]弦论(SAM+dfs)

    3998: [TJOI2015]弦论 题目:传送门 题解: SAM的入门题目(很好的复习了SAM并加强Right集合的使用) 其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接 ...

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

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

  4. Luogu P3975 [TJOI2015]弦论

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

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

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

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

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

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

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

  8. luogu P3975 [TJOI2015]弦论 SAM

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

  9. LGOJ3975 TJOI2015 弦论

    link:TJOI2015 弦论 题目大意: 给定一个字符串,输出在对该字符串所有的非空子串排序后第\(k\)个 另外的一个限制是\(T\):子串本质相同但位置不同算\(1\)或多个 \(|s| \l ...

随机推荐

  1. python之 《zip,lambda, map》

    1.zip 对于zip我们一般都是用在矩阵上 eg: a = [1,2,3] b = ['a', 'b', 'c'] x = zip(a, b) print(x) print(list(x)) 结果是 ...

  2. parted分区对齐

    分区提示未对齐 [root@lab8106 ceph]# parted /dev/sdd GNU Parted 3.1 Using /dev/sdd Welcome to GNU Parted! Ty ...

  3. Nginx实例

    一.反向代理 反向代理实例一 1.实现效果 打开浏览器,在浏览器地址栏输入地址www.pluto.com,跳转到 liunx 系统 tomcat 主页面中 2.准备工作 [1].安装tomcat [r ...

  4. DockerPush

    1.阿里云镜像发布流程 2.镜像生成 语法:docker commit [OPTIONS] 容器ID [REPOSITORY[:TAG]] [root@pluto data]# docker imag ...

  5. txt文件覆盖恢复

    1.txt文件恢复到之前保存的版本 2.电脑未重启 方式:如果你使用系统还原可以用"还原以前的版本"功能来轻松找回. 右击.txt文件-还原以前的版本-选择时间点-还原

  6. js-根据日期获取本年所有周日

    /** * 方法 描述 Date() 返回当日的日期和时间. getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31). getDay() 从 Date 对象返回一周中的某一天 ( ...

  7. .net core quartz job作业调度管理组件

    定时作业对于多数系统来说,都会用到,定时作业的实现方式可以有多种方式,简单的方式用Timer就可以实现,但是达不到通用的效果,本文采用Quartz基础组件来搭建一套企业通用的作业调度管理服务,希望对于 ...

  8. MyBatis学习02

    3.增删改查实现 select select标签是mybatis中最常用的标签之一 select语句有很多属性可以详细配置每一条SQL语句 SQL语句返回值类型.[完整的类名或者别名] 传入SQL语句 ...

  9. FL studio系列教程(三):如何用FL Studio做电音

    电音制作,自然少不了适合做电音的软件,市面上可以进行电音制作的软件不少,可是如果在这些软件中只能选择一款的话,想必多数人会把票投给FL Studio,毕竟高效率是永远不变的真理,今天就让我们来看看如何 ...

  10. workerman windows环境下无法启动问题

    问题描述 使用laravel框架composer加载workerman/gateway-worker扩展使用workerman做客服系统.通过laravel的command命令: php artisa ...