洛谷 - P3975 [TJOI2015]弦论(后缀自动机)
题目链接:点击查看
题目大意:给出一个字符串 s,再给出一次询问,询问分为两种类型:
- 0 k:如果不同位置的相同子串算作一个,求第 k 小的子串
- 1 k:如果不同位置的相同子串算作多个,求第 k 小的子串
题目分析:温故而知新,稍微总结一下这次重学 SAM 的一点小收货吧:
- SAM 中的连边只有两种,一种是 trie 图上的,一种是 parent 树上的,前者是 DAG,后者是树
- 一般看到 SAM 会配合基数排序然后倒着维护答案,这个过程实际上模拟的是在 parent 树上的 dfs,更直观的理解就是,将 parent 树建出来,然后直接在树上维护信息即可
- 如果想要维护 dp 的话,在 trie 图上需要在 DAG 上跑拓扑,在 parent 树上跑树形 dp
- 顺着 trie 图跑的话可以得到子串的前缀,顺着 parent 树往上跳 father 的话可以得到后缀的后缀(应该可以这样理解)
回到这个题目中来,第一个问题就是如何在 SAM 上表示子串,因为从源点开始顺着 trie 图一直走,每到一个新的节点就代表着一个新的子串,所以在 trie 图上拓扑排序一下求出可达的路径数,实质上就是求出了从某个点开始,继续走下去可以到达的子串个数
第二个问题就是,如何区分 “位置不同的相同子串算一个” 和 “位置不同的相同子串算多个”,首先对于 trie 图而言,其每个不同的节点都代表着一个本质不同的子串,所以初始时将每个节点的 cnt[ i ] 置为 1,然后在 DAG 上拓扑计算一下路径的个数就好了
如果位置不同的串算多个,也就是说需要额外维护一下每个节点 endpos 的个数,因为 endpos 的定义就是 “以当前节点为后缀在原串中出现的位置 的 集合”,维护 endpos 位置的个数这个在构建自动机时,令所有非复制而来的节点赋值为 1 ,最后在 parent 树上 dp 一下就好了,然后再同上在 trie 图上拓扑一下就能求出路径个数
至于求解的话,dfs 贪心去找合适的路径就可以了
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;char s[N];LL cnt[N<<1],sum[N<<1];vector<int>node[N<<1];bool vis[N<<1];struct Node
{int ch[26];int fa,len;
}t[N<<1];int tot,last;int newnode()
{tot++;for(int i=0;i<26;i++)t[tot].ch[i]=0;t[tot].fa=t[tot].len=0;return tot;
}void add(int x)
{int p=last,np=last=newnode();t[np].len=t[p].len+1;cnt[np]=1;while(p&&!t[p].ch[x])t[p].ch[x]=np,p=t[p].fa;if(!p)t[np].fa=1;else{int q=t[p].ch[x];if(t[p].len+1==t[q].len)t[np].fa=q;else{int nq=newnode();t[nq]=t[q];t[nq].len=t[p].len+1;t[q].fa=t[np].fa=nq;while(p&&t[p].ch[x]==q)t[p].ch[x]=nq,p=t[p].fa;}}
}void dfs1(int u)//parent树上dp
{for(auto v:node[u]){dfs1(v);cnt[u]+=cnt[v];}
}void dfs2(int u)//DAG上dp
{if(vis[u])return;vis[u]=true;for(int i=0;i<26;i++){if(!t[u].ch[i])continue;dfs2(t[u].ch[i]);sum[u]+=sum[t[u].ch[i]];}
}void print(int u,int k)
{if(k<=cnt[u])return;k-=cnt[u];for(int i=0;i<26;i++){int v=t[u].ch[i];if(!v)continue;if(sum[v]<k){k-=sum[v];continue;}putchar('a'+i);print(v,k);return;}
}void init()
{last=1;tot=0;newnode();
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int op,k;init();scanf("%s%d%d",s,&op,&k);int n=strlen(s);for(int i=0;i<n;i++)add(s[i]-'a');for(int i=1;i<=tot;i++)node[t[i].fa].push_back(i);dfs1(1);for(int i=1;i<=tot;i++)sum[i]=op?cnt[i]:(cnt[i]=1);sum[1]=cnt[1]=0;dfs2(1);if(k>sum[1])puts("-1");elseprint(1,k);return 0;
}
洛谷 - P3975 [TJOI2015]弦论(后缀自动机)相关推荐
- 洛谷_3975 [TJOI2015]弦论(后缀自动机)
[TJOI2015]弦论 题目链接:https://www.luogu.com.cn/problem/P3975 题解: 对于T==0,只需要构造自动机,将每个状态节点的cnt设为1,然后DFS即可. ...
- 洛谷 [P3975 [TJOI2015]弦论
洛谷 P3975 [TJOI2015]弦论 题目描述 给定一个长度为 nnn 的字符串,求它的第 kkk 小字串:给定 ttt, ttt 为 000 则表示不同位置的相同子串算作一个,ttt 为 11 ...
- 洛谷 P3975 [TJOI2015]弦论 解题报告
P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...
- 洛谷P3975 - [TJOI2015]弦论
Portal Description 给出一个小写字母串\(s(|s|\leq5\times10^5),t\in\{0,1\},k(k\leq10^9)\),求\(s\)的第\(k\)小子串.\(t= ...
- 【题解】P3975 [TJOI2015]弦论 后缀自动机
给定一个长度为n(5e5)的字符串,求它字典序第k小的子串. 输入还有一个t,t=0时表示不同位置的相同子串算作一个,t=1表示不同位置的相同子串算作多个. 使用后缀自动机. 后缀自动机中的每条路径对 ...
- 洛谷P3975 [TJOI2015]弦论
链接 点击跳转 题解 后缀自动机上每条路径都是一个子串 对于t=0t=0t=0,实际上是统计字典序第kkk小的子串,自下向上进行一个dpdpdp就可以解决这个问题 对于t=1t=1t=1,每条路径都被 ...
- [洛谷P3975][TJOI2015]弦论
题目大意:求一个字符串的第$k$大字串,$t$表示长得一样位置不同的字串是否算多个 题解:$SAM$,先求出每个位置可以到达多少个字串($Right$数组),然后在转移图上$DP$,若$t=1$,初始 ...
- P3975 [TJOI2015]弦论 - 后缀自动机(SAM)
这是一道板子题的改编,意在加深对求第k小子串的理解.首先先看一下最简单的SAM板子.相信应该都写过了才会写这题 // // Created by acer on 2021/2/16. // //判断子 ...
- bzoj3998/洛谷3975 [TJOI2015]弦论 (后缀自动机)
bzoj3998/洛谷3975 [TJOI2015]弦论 题意:求第 k 小字串 方法:先建立SAM,和上一题一样按照拓扑序求出size(表示这个串出现的次数).这道题我们需要求第 k 小,所以我们还 ...
最新文章
- 微信为什么不丢消息?
- python怎么筛选excel数据_PythonEXCEL读取-保存-矩阵合并-条件筛选
- c语言最小点对问题_C语言教程(一):程序开发理论基础!
- python 课程设计 夏敏捷_Python课程设计(微课视频版21世纪高等学校通识教育规划教材)/计算机技术入门丛书...
- 图标设计素材|解析UI设计图标
- jquery之超简单的div显示和隐藏特效demo
- Comet服务器推送与SignalR
- 中国AI公司公开击败谷歌微软,这次是Yi+AI视觉团队
- asp.net MD5数据加密和解密
- 硬件开发笔记(二):硬件开发基本流程,制作一个USB转RS232的模块(一):开发基本过程和元器件选型
- 静止、极轨卫星遥感图像太阳及卫星天顶、方位角的计算
- python画江苏_江苏高考数学再现算法流程图!学过编程的孩子都说So easy!
- [pytorch] torch.cuda.FloatTensor报错
- python print()什么意思_python print用法是什么
- CTEX中文编辑问题
- 中国染料医用激光器行业市场供需与战略研究报告
- 阿里云申请免费SSL证书
- 【论文写作】Latex在线创作工具overleaf及账号注册办法
- golang导入私有仓库报错:“server response: not found:xxx: invalid version: git ls-remote -q origin in xxx“
- HC-SR04超声波模块的使用(stm32f103c8t6/HAL)(超详细)
热门文章
- java实现js取反_特定位取反(js实现)
- c#保存数据格式为.cvs_C#读取csv格式文件的方法
- 扩展 日历_日历功能的拓展:Apple Watch能在屏幕上显示年日历
- 酒店管理系统c语言带注释,酒店管理系统--C语言版.pdf
- html左右滑动选择控件,jQuery让控件左右移动的三种实现方法
- 创建调用查询删除存储过程语法
- Nginx的rewrite之if指令(二)
- Nginx配置成系统服务
- synchronized同步方法
- 简单了解各种序列化技术-JSON序列化框架