这是一道板子题的改编,意在加深对求第k小子串的理解。首先先看一下最简单的SAM板子。相信应该都写过了才会写这题

//
// Created by acer on 2021/2/16.
//
//判断子串,不同子串个数,所有子串字典序第i大,最长公共子串#include <cstring>
#include <iostream>
#include "string"#define mem(x, i) memset(x,i,sizeof(x))
using namespace std;
const int MAXN = 3e5 + 10;int len[MAXN << 1];
int ch[MAXN << 1][27];
int fa[MAXN << 1];
int last = 1;
int tot = 1;
int p;void add(int c) {p = last;last = ++tot;int np = last;len[np] = len[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 (len[q] == len[p] + 1) fa[np] = q;else {int nq = ++tot;memcpy(ch[nq], ch[q], sizeof(ch[nq]));fa[nq] = fa[q];len[nq] = len[p] + 1;fa[np] = fa[q] = nq;for (; p && ch[p][c] == q; p = fa[p]) {ch[p][c] = nq;}}}
}char s[MAXN];
int count[MAXN<<1];
void dfs(int k)
{if (count[k]) return;count[k] = 1;for (int i = 0; i <= 26; ++i) {          //此处是实现算法的关键,通过从a遍历到z来计算字典序if (!ch[k][i]) continue;dfs(ch[k][i]);count[k]+=count[ch[k][i]];       //很明显,count的意义就是字典序为k的子串的首字母}
}
int main() {scanf("%s", s + 1);int l = strlen(s + 1);for (int i = 1; i <= l; ++i) {add(s[i] - 'a');}int T;dfs(1);scanf("%d",&T);while (T--){int k;int now = 1;scanf("%d",&k);while (k){if (!k) break;for (int i = 0; i <= 26; ++i) {if (ch[now][i]) {if (count[ch[now][i]] >= k) {     //因为不能往前找,所以找到的第一个大于k的就肯定是属于答案的子串中putchar(i+'a');--k;now = ch[now][i];break;} elsek-=count[ch[now][i]];       //这个字母包含的不够多}}}puts("");}
}

这套代码对应的题目的一个子问题,即找本质不同子串的情况,那么对于加上相同字串的贡献时,我们可以维护一个数组记录每个节点的子串重复出现了几次,同时用这个数组取替换count数组的值就行了。

//
// Created by acer on 2021/2/16.
//
//判断子串,不同子串个数,所有子串字典序第i大,最长公共子串#include <cstring>
#include <iostream>
#include "string"#define mem(x, i) memset(x,i,sizeof(x))
using namespace std;
const int MAXN = 3e5 + 10;int len[MAXN << 1];
int ch[MAXN << 1][27];
int fa[MAXN << 1];
int siz[MAXN << 1];
int last = 1;
int tot = 1;
int p;void add(int c) {p = last;last = ++tot;int np = last;len[np] = len[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 (len[q] == len[p] + 1) fa[np] = q;else {int nq = ++tot;memcpy(ch[nq], ch[q], sizeof(ch[nq]));fa[nq] = fa[q];len[nq] = len[p] + 1;fa[np] = fa[q] = nq;for (; p && ch[p][c] == q; p = fa[p]) {ch[p][c] = nq;}}}siz[last] =1;
}int id[MAXN<<1];
int weig[MAXN << 1];
void getTuopu()
{for (int k = 1; k <= tot; ++k)        //给每个点赋权值weig[len[k]] ++;for (int m = 1; m <= tot; ++m)      //获得长度大于等于m的点的总个数,所以必然是不会相同的,故可以用来标记。weig[m] += weig[m-1];for (int n = 1; n <= tot; ++n)   //根据点出现顺序获得拓扑序id[weig[len[n]]--] = n;
}
char s[MAXN];
int count[MAXN << 1];
int t;
void dfs(int k) {if (count[k]) return;if (!t)count[k] = 1;elsecount[k] = siz[k];for (int i = 0; i <= 26; ++i) {          //此处是实现算法的关键,通过从a遍历到z来计算字典序if (!ch[k][i]) continue;dfs(ch[k][i]);count[k] += count[ch[k][i]];       //很明显,count的意义就是字典序为k的子串的首字母}
}int main() {scanf("%s", s + 1);int l = strlen(s + 1);for (int i = 1; i <= l; ++i) {add(s[i] - 'a');}getTuopu();for (int i = tot; i > 1; --i) {siz[fa[id[i]]] += siz[id[i]];}int k;int now = 1;cin >> t >> k;dfs(1);while (k) {if (!k) break;for (int i = 0; i <= 26; ++i) {if (ch[now][i]) {//                cout << ch[now][i]  << ' ' << char(i + 'a') << endl;if (count[ch[now][i]] >= k) {     //因为不能往前找,所以找到的第一个大于k的就肯定是属于答案的子串中putchar(i + 'a');now = ch[now][i];k -= (t==1?siz[now]:1);break;} elsek -= count[ch[now][i]];       //这个字母包含的不够多}}}puts("");}

P3975 [TJOI2015]弦论 - 后缀自动机(SAM)相关推荐

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

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

  2. 洛谷 - P3975 [TJOI2015]弦论(后缀自动机)

    题目链接:点击查看 题目大意:给出一个字符串 s,再给出一次询问,询问分为两种类型: 0 k:如果不同位置的相同子串算作一个,求第 k 小的子串 1 k:如果不同位置的相同子串算作多个,求第 k 小的 ...

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

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

  4. [TJOI2015]弦论 后缀自动机

    字典序第 k 大 沿着自动机的边走即可,比较水的一道题吧. Code: #include <cstdio> #include <algorithm> #include < ...

  5. luogu P3975 [TJOI2015]弦论 SAM

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

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

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

  7. 洛谷 [P3975 [TJOI2015]弦论

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

  8. 后缀自动机(SAM)讲解 + Luogu p3804【模板】后缀自动机 (SAM)

    本文求节点子串长度最小值有点问题,现已修改. SAM 后缀自动机可以存储某一个字符串的所有子串. 一.概念 下图是一个 字符串 "aababa" 的 后缀自动机. 上图中的 黑色边 ...

  9. JZOJ4025. 【佛山市选2015】找回密码(后缀自动机SAM)

    题目描述 Description Kevin是一个热爱字符串的小孩.有一天,他把自己的微信登录密码给忘记了,万般无奈之下只好点"找回密码". 这时候,网页上出现了当初设定的密保问题 ...

最新文章

  1. python merge很费内存吗,python如何使用merge实现堆
  2. poj 1987 树的分治
  3. RabbitMQ快速安装配置指南
  4. 使用 做签名的post_基础实操|使用jmeter对聊天软件进行接口测试
  5. 怎么做圆形二维码_圆形吊顶怎么做?装修网盘点圆形吊顶安装注意事项
  6. HTTPRunner学习笔记
  7. 2018年php还是python好_2018年PHP还值得学习吗?
  8. 【信息系统项目管理师】第2章-信息系统项目管理基础 知识点详细整理
  9. atitit js 开发工具 ide的代码结构显示(func list) outline总结
  10. 亿图图示(edrawmax中文版) v10.1.7pjb
  11. 盘点(腾讯字节谷歌等大厂)面试中常见的智力题
  12. Add Juniper SRX Cluster into JunOS Space 16.1 Security Director
  13. PGP验证数字签名原理
  14. 自动html5视频播放插件,视频自动转HTML5播放器插件
  15. lazy java_深入理解 Spring @Lazy 注解以及最佳实践
  16. 解密中概股的“杀手”:做空者
  17. mysql中的/、div的区别
  18. php 蛋糕一刀均分试题,5个小朋友分一个蛋糕,只准切三刀,该怎样才能平分
  19. Dijkstra 路径规划算法在二维仿真环境中的应用 -- Python代码实现
  20. 我也是从寒门走出来的,程序员这个职业是我最好的选择!

热门文章

  1. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗 ☞ 异步历险奇遇记...
  2. (已更新)新版帝国cms内核试玩佣金WAP手机版网站源码,可打包APP
  3. 世界三大顶级音响_世界音响三个顶尖品牌 世界顶级音响哪个品牌好
  4. android分辨率 尺寸 dpi换算
  5. matlab让legend横着排,echarts画一个饼图, orient: 'horizontal',图例横向排列,当图例比较多时,如何在第二排让图标一一对齐?...
  6. firefox 配置
  7. Windows压缩工具 “ Bandizip 与 7-zip ”
  8. 计算机图形学——MFC绘图基础
  9. python中_x、__x、__xx__的区别
  10. 这4款神级软件 最好用的软件