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. yum 的一些问题总结

    1. yum 只删除目标,不删除依赖 rpm -e --nodeps xxx 2.yum remove 出错 报错 Error: Cannot retrieve repository metadata ...

  2. Python_多进程_pool进程池

    多进程典型案例: 1.将子进程的进程名作为列表中的元素,在父进程中遍历(异步)执行 #coding: utf-8 from multiprocessing import Pool import os, ...

  3. arm-linux校时和时钟同步

    # 将时间写到系统 date 2020.08.25-14:02:00 # 将时间同步到硬件时钟芯片 hwclock -f /dev/rtc1 -w # 将时间从硬件时钟芯片同步到系统 hwclock ...

  4. vue实现增删改查(内附源代码)

    VUE+Element实现增删改查 @ 目录 VUE+Element实现增删改查 前言 实验步骤 总结: 源代码 前言 &最近因为一些原因,没有更博客,昨天老师布置了一个作业,用vue实现增删 ...

  5. 使用pdfFactory为PDF文件设定查看选项

    一般情况下,大部分PDF文件都会按照默认的查看设置,以100%的尺寸显示第一页的内容.但在一些特殊情况下,PDF文件的创建者会设定其他的文件查看尺寸,或设定打开页为第N页,来达到引起阅读者关注的目的. ...

  6. python自动化测试pytest框架

    pytest和unittest都是python中的测试框架,pytest相比unittest 更加的灵活,具体体现在 以下几点 1.写测试方法时不用继承类 2.前置后置放在一起 2.1如果是全局共享的 ...

  7. ThreadLocal以及强软弱虚引用

    1.ThreadLocal ThreadLocal即线程本地,可以实现每个线程存入取出TreadLocal值互不影响.因为TheadLocal底层是用了一个Map结构存放数据,而这个Map是从当前这个 ...

  8. 记录一次tomcat问题排查记录:org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19

    最近项目升级jdk,从jdk7 升级到 jdk8,本地已经自测完成了,需要部署到测试环境,测试环境已经装好 jdk8 了,但是tomcat 的版本还是 7.不过,据我之前了解,tomcat7是可以运行 ...

  9. 墨振文档__UnitTest断言

    unittest 断言方法 断言:自动化测试主要用于对流程来进行测试,断言就是自动化测试中的预期结果与实际结果的对比.

  10. 转载的一篇文章eclipse添加插件

    eclipse没有(添加)"Dynamic Web Project"选项的方法 转载海边的第八只螃蟹 最后发布于2015-11-24 21:24:15 阅读数 40814  收藏 ...