P3975 [TJOI2015]弦论 - 后缀自动机(SAM)
这是一道板子题的改编,意在加深对求第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)相关推荐
- 【题解】P3975 [TJOI2015]弦论 后缀自动机
给定一个长度为n(5e5)的字符串,求它字典序第k小的子串. 输入还有一个t,t=0时表示不同位置的相同子串算作一个,t=1表示不同位置的相同子串算作多个. 使用后缀自动机. 后缀自动机中的每条路径对 ...
- 洛谷 - P3975 [TJOI2015]弦论(后缀自动机)
题目链接:点击查看 题目大意:给出一个字符串 s,再给出一次询问,询问分为两种类型: 0 k:如果不同位置的相同子串算作一个,求第 k 小的子串 1 k:如果不同位置的相同子串算作多个,求第 k 小的 ...
- 洛谷_3975 [TJOI2015]弦论(后缀自动机)
[TJOI2015]弦论 题目链接:https://www.luogu.com.cn/problem/P3975 题解: 对于T==0,只需要构造自动机,将每个状态节点的cnt设为1,然后DFS即可. ...
- [TJOI2015]弦论 后缀自动机
字典序第 k 大 沿着自动机的边走即可,比较水的一道题吧. Code: #include <cstdio> #include <algorithm> #include < ...
- luogu P3975 [TJOI2015]弦论 SAM
luogu P3975 [TJOI2015]弦论 链接 bzoj 思路 建出sam. 子串算多个的,统计preant tree的子树大小,否则就是大小为1 然后再统计sam的节点能走到多少串. 然后就 ...
- 洛谷 P3975 [TJOI2015]弦论 解题报告
P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...
- 洛谷 [P3975 [TJOI2015]弦论
洛谷 P3975 [TJOI2015]弦论 题目描述 给定一个长度为 nnn 的字符串,求它的第 kkk 小字串:给定 ttt, ttt 为 000 则表示不同位置的相同子串算作一个,ttt 为 11 ...
- 后缀自动机(SAM)讲解 + Luogu p3804【模板】后缀自动机 (SAM)
本文求节点子串长度最小值有点问题,现已修改. SAM 后缀自动机可以存储某一个字符串的所有子串. 一.概念 下图是一个 字符串 "aababa" 的 后缀自动机. 上图中的 黑色边 ...
- JZOJ4025. 【佛山市选2015】找回密码(后缀自动机SAM)
题目描述 Description Kevin是一个热爱字符串的小孩.有一天,他把自己的微信登录密码给忘记了,万般无奈之下只好点"找回密码". 这时候,网页上出现了当初设定的密保问题 ...
最新文章
- python merge很费内存吗,python如何使用merge实现堆
- poj 1987 树的分治
- RabbitMQ快速安装配置指南
- 使用 做签名的post_基础实操|使用jmeter对聊天软件进行接口测试
- 怎么做圆形二维码_圆形吊顶怎么做?装修网盘点圆形吊顶安装注意事项
- HTTPRunner学习笔记
- 2018年php还是python好_2018年PHP还值得学习吗?
- 【信息系统项目管理师】第2章-信息系统项目管理基础 知识点详细整理
- atitit js 开发工具 ide的代码结构显示(func list) outline总结
- 亿图图示(edrawmax中文版) v10.1.7pjb
- 盘点(腾讯字节谷歌等大厂)面试中常见的智力题
- Add Juniper SRX Cluster into JunOS Space 16.1 Security Director
- PGP验证数字签名原理
- 自动html5视频播放插件,视频自动转HTML5播放器插件
- lazy java_深入理解 Spring @Lazy 注解以及最佳实践
- 解密中概股的“杀手”:做空者
- mysql中的/、div的区别
- php 蛋糕一刀均分试题,5个小朋友分一个蛋糕,只准切三刀,该怎样才能平分
- Dijkstra 路径规划算法在二维仿真环境中的应用 -- Python代码实现
- 我也是从寒门走出来的,程序员这个职业是我最好的选择!
热门文章
- springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗 ☞ 异步历险奇遇记...
- (已更新)新版帝国cms内核试玩佣金WAP手机版网站源码,可打包APP
- 世界三大顶级音响_世界音响三个顶尖品牌 世界顶级音响哪个品牌好
- android分辨率 尺寸 dpi换算
- matlab让legend横着排,echarts画一个饼图, orient: 'horizontal',图例横向排列,当图例比较多时,如何在第二排让图标一一对齐?...
- firefox 配置
- Windows压缩工具 “ Bandizip 与 7-zip ”
- 计算机图形学——MFC绘图基础
- python中_x、__x、__xx__的区别
- 这4款神级软件 最好用的软件