题目链接:点击查看

题目大意:给出一个字符串 s,再给出一次询问,询问分为两种类型:

  1. 0 k:如果不同位置的相同子串算作一个,求第 k 小的子串
  2. 1 k:如果不同位置的相同子串算作多个,求第 k 小的子串

题目分析:温故而知新,稍微总结一下这次重学 SAM 的一点小收货吧:

  1. SAM 中的连边只有两种,一种是 trie 图上的,一种是 parent 树上的,前者是 DAG,后者是树
  2. 一般看到 SAM 会配合基数排序然后倒着维护答案,这个过程实际上模拟的是在 parent 树上的 dfs,更直观的理解就是,将 parent 树建出来,然后直接在树上维护信息即可
  3. 如果想要维护 dp 的话,在 trie 图上需要在 DAG 上跑拓扑,在 parent 树上跑树形 dp
  4. 顺着 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]弦论(后缀自动机)相关推荐

  1. 洛谷_3975 [TJOI2015]弦论(后缀自动机)

    [TJOI2015]弦论 题目链接:https://www.luogu.com.cn/problem/P3975 题解: 对于T==0,只需要构造自动机,将每个状态节点的cnt设为1,然后DFS即可. ...

  2. 洛谷 [P3975 [TJOI2015]弦论

    洛谷 P3975 [TJOI2015]弦论 题目描述 给定一个长度为 nnn 的字符串,求它的第 kkk 小字串:给定 ttt, ttt 为 000 则表示不同位置的相同子串算作一个,ttt 为 11 ...

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

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

  4. 洛谷P3975 - [TJOI2015]弦论

    Portal Description 给出一个小写字母串\(s(|s|\leq5\times10^5),t\in\{0,1\},k(k\leq10^9)\),求\(s\)的第\(k\)小子串.\(t= ...

  5. 【题解】P3975 [TJOI2015]弦论 后缀自动机

    给定一个长度为n(5e5)的字符串,求它字典序第k小的子串. 输入还有一个t,t=0时表示不同位置的相同子串算作一个,t=1表示不同位置的相同子串算作多个. 使用后缀自动机. 后缀自动机中的每条路径对 ...

  6. 洛谷P3975 [TJOI2015]弦论

    链接 点击跳转 题解 后缀自动机上每条路径都是一个子串 对于t=0t=0t=0,实际上是统计字典序第kkk小的子串,自下向上进行一个dpdpdp就可以解决这个问题 对于t=1t=1t=1,每条路径都被 ...

  7. [洛谷P3975][TJOI2015]弦论

    题目大意:求一个字符串的第$k$大字串,$t$表示长得一样位置不同的字串是否算多个 题解:$SAM$,先求出每个位置可以到达多少个字串($Right$数组),然后在转移图上$DP$,若$t=1$,初始 ...

  8. P3975 [TJOI2015]弦论 - 后缀自动机(SAM)

    这是一道板子题的改编,意在加深对求第k小子串的理解.首先先看一下最简单的SAM板子.相信应该都写过了才会写这题 // // Created by acer on 2021/2/16. // //判断子 ...

  9. bzoj3998/洛谷3975 [TJOI2015]弦论 (后缀自动机)

    bzoj3998/洛谷3975 [TJOI2015]弦论 题意:求第 k 小字串 方法:先建立SAM,和上一题一样按照拓扑序求出size(表示这个串出现的次数).这道题我们需要求第 k 小,所以我们还 ...

最新文章

  1. 微信为什么不丢消息?
  2. python怎么筛选excel数据_PythonEXCEL读取-保存-矩阵合并-条件筛选
  3. c语言最小点对问题_C语言教程(一):程序开发理论基础!
  4. python 课程设计 夏敏捷_Python课程设计(微课视频版21世纪高等学校通识教育规划教材)/计算机技术入门丛书...
  5. 图标设计素材|解析UI设计图标
  6. jquery之超简单的div显示和隐藏特效demo
  7. Comet服务器推送与SignalR
  8. 中国AI公司公开击败谷歌微软,这次是Yi+AI视觉团队
  9. asp.net MD5数据加密和解密
  10. 硬件开发笔记(二):硬件开发基本流程,制作一个USB转RS232的模块(一):开发基本过程和元器件选型
  11. 静止、极轨卫星遥感图像太阳及卫星天顶、方位角的计算
  12. python画江苏_江苏高考数学再现算法流程图!学过编程的孩子都说So easy!
  13. [pytorch] torch.cuda.FloatTensor报错
  14. python print()什么意思_python print用法是什么
  15. CTEX中文编辑问题
  16. 中国染料医用激光器行业市场供需与战略研究报告
  17. 阿里云申请免费SSL证书
  18. 【论文写作】Latex在线创作工具overleaf及账号注册办法
  19. golang导入私有仓库报错:“server response: not found:xxx: invalid version: git ls-remote -q origin in xxx“
  20. HC-SR04超声波模块的使用(stm32f103c8t6/HAL)(超详细)

热门文章

  1. java实现js取反_特定位取反(js实现)
  2. c#保存数据格式为.cvs_C#读取csv格式文件的方法
  3. 扩展 日历_日历功能的拓展:Apple Watch能在屏幕上显示年日历
  4. 酒店管理系统c语言带注释,酒店管理系统--C语言版.pdf
  5. html左右滑动选择控件,jQuery让控件左右移动的三种实现方法
  6. 创建调用查询删除存储过程语法
  7. Nginx的rewrite之if指令(二)
  8. Nginx配置成系统服务
  9. synchronized同步方法
  10. 简单了解各种序列化技术-JSON序列化框架