CF1037H Security题解
根据字典序的定义,位置大的大于长度长的,长度长的大于长度短的。
所以我们贪心,先追求长度长的,再追求后面的位置大的,再追求前面的位置大的。
我们要一个能遍历子串的结构,就选 SAM 得了。
还有个限制:为 S[L...R] 的字串。正好 SAM 有个东西叫做 endpos,用线段树合并求一下就求出来了。
upd on 2023.2.3:我觉得还是得细嗦一下线段树合并。具体原因是我突然发现这好像已经是一个常用套路(君の名は)
parent树上统计元素,权值线段树+动态开点这一套组合拳应该没啥问题。但假如手玩一棵 endpos 树就会发现有一些元素分着分着就不见了。原因是长度不允许这个点作最后一个字符。
于是我们按前缀插入SAM时就在这 \(n\) 个点上建 \(n\) 颗只有当前下标的线段树(在它的子树中这个元素由于长度原因不会再出现),然后正常合并即可。
这里我们是不能直接合并的,因为之后需要再次合并,直接合并会破坏其中一棵线段树,我们采用新开节点的方式即可。
看着空间不对,但实际上一次 merge 函数只会加一个点,所以空间复杂度与时间复杂度同阶,可以通过。
注意当当前字符串长度为 \(len\) 时,endpos 要有 S[L+len...R] 才行。因为 endpos统计的是结尾。
再提一嘴,要一直保证 endpos 的条件成立,不行就退出。否则会超时。
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
#define mp make_pair
#define forp(i, a, b) for(int i = (a);i <= (b);i ++)
using namespace std;
const int maxn = 2e7 + 5;
int n;
string T;int L, R;
namespace linetree
{
int ch[8000005][2], rt[200005], tot;
void make(int p, int i)
{
rt[p] = ++ tot;int u = rt[p], l = 1, r = n;
while(1)
{
int mid = l + r >> 1;
if(l == r) break;
(mid >= i) ? (r = mid, u = ch[u][0] = ++ tot) : (l = mid + 1, u = ch[u][1] = ++ tot);
}
// cout << l << ' ' << r << ' ' << i << ' ' << u << endl;
}
int merge(int u, int v, int len) {
if (!u || !v) return u += v;
int p = ++ tot;
if (len != 1) ch[p][0] = merge(ch[u][0], ch[v][0], len + 1 >> 1), ch[p][1] = merge(ch[u][1], ch[v][1], len >> 1);
return p;
}
int query(int p, int l, int r, int fl, int fr)
{
if(fl <= l && fr >= r) return 1;
int mid = l + r >> 1, ans = 0;
if(fl <= mid && ch[p][0]) ans |= query(ch[p][0], l, mid, fl, fr);
if(fr >= mid + 1 && ch[p][1]) ans |= query(ch[p][1], mid + 1, r, fl, fr);
return ans;
}
}
int c[100005], fin;
namespace SAM
{
struct point{ int ch[26], fa, len; }a[200005];
int las = 1, tot = 1;
ll f[200005];
void add(int s, int i)
{
int p = las, np = las = ++ tot;f[np] = 1;
linetree::make(np, i);
a[np].len = a[p].len + 1;
for(;p && a[p].ch[s] == 0;p = a[p].fa) a[p].ch[s] = np;
if(p == 0){ a[np].fa = 1; return ; }
int q = a[p].ch[s];
if(a[q].len == a[p].len + 1){ a[np].fa = q; return ; }
int nq = ++ tot;a[nq] = a[q];
a[q].fa = a[np].fa = nq;
a[nq].len = a[p].len + 1;
for(;p && a[p].ch[s] == q;p = a[p].fa) a[p].ch[s] = nq;
}
bool run(int i, int u, int pd)
{
if(u == 0) return 0;
if(linetree::query(linetree::rt[u], 1, n, L + (i - 1) - 1, R) == 0) return 0;
if(pd == 1) return 1;
if(i == T.size() + 1)
{
forp(j, 0, 25){ c[i] = j; if(run(i + 1, a[u].ch[j], 1)){ fin = i; return 1; } }
return 0;
}
c[i] = T[i - 1] - 'a'; if(run(i + 1, a[u].ch[T[i - 1] - 'a'], 0)) return 1;
forp(j, T[i - 1] - 'a' + 1, 25){ c[i] = j; if(run(i + 1, a[u].ch[j], 1)){fin = i; return 1;} }
return 0;
}
vector<int> e[200005];
void init(){ forp(i, 2, tot) e[a[i].fa].push_back(i); }
void build(int u)
{
for(auto v : e[u])
{
build(v);
linetree::rt[u] = linetree::merge(linetree::rt[u], linetree::rt[v], n);
}
}
}
signed main()
{
freopen("text.in", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string s;
cin >> s;
n = s.size();
forp(i, 1, s.size()) SAM::add(s[i - 1] - 'a', i);
SAM::init();SAM::build(1);
int q;
cin >> q;
// cout << "#### " << linetree::query(linetree::rt[4], 1, n, 3, 3) << endl;
while(q --)
{
cin >> L >> R >> T;
if(SAM::run(1, 1, 0))
{
// if(c[3] == 'm' - 'a' && c[2] == 'y' - 'a' && c[1] == 'o' - 'a' && fin == 3) cout << "#### " << T << endl;
forp(i, 1, fin) cout << char(c[i] + 'a');
cout << endl;
}
else cout << -1 << endl;
}
return 0;
}
CF1037H Security题解的更多相关文章
- CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心
题目描述: 给定一个字符串 $S$ 给出 $Q$ 个操作,给出 $L,R,T$,求出字典序最小的 $S_{1}$ 为 $S[L...R]$的子串,且 $S_{1}$ 的字典序严格大于 $T$. 输出这 ...
- CF1037H Security 线段树合并 SAM
LINK:Security 求一个严格大于T的字符串 是原字符串S[L,R]的子串. 容易想到尽可能和T相同 然后再补一个尽可能小的字符即可. 出于这种思想 可以在SAM上先跑匹配 然后枚举加哪个字符 ...
- [CF1037H] Security
题目链接 codeforces. 洛谷. Solution 按照套路,可以\(SAM\)上线段树合并求出\(endpos\)集合,然后随便贪心一下就好了. #include<bits/stdc+ ...
- CF1037H Security——SAM+线段树合并
又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...
- SAM 做题笔记(各种技巧,持续更新,SA)
SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...
- [笔记] 后缀自动机 (SAM)
实现 void ins(int c){ int np = ++dcnt, p = lst; lst = np; t[np].len = t[p].len + 1, t[np].eps = 1; whi ...
- UVA 10318 Security Panel(DFS剪枝 + 状压 + 思维)题解
题意:给一个r*c的矩阵开关(初始全打开的),每次按下一个开关都会改变3*3范围内的有*的地方的状态,问你最少几步能让开关全闭上,按升序输出按哪些按钮 思路:每个按钮至多按一下,按按钮的顺序和结果无关 ...
- dfs | Security Badges
Description You are in charge of the security for a large building, with n rooms and m doors between ...
- Codeforces 948D Perfect Security(字典树)
题目链接:Perfect Security 题意:给出N个数代表密码,再给出N个数代表key.现在要将key组排序,使key组和密码组的亦或所形成的组字典序最小. 题解:要使密码组里面每个数都找到能使 ...
- leetcode & lintcode 题解
刷题备忘录,for bug-free 招行面试题--求无序数组最长连续序列的长度,这里连续指的是值连续--间隔为1,并不是数值的位置连续 问题: 给出一个未排序的整数数组,找出最长的连续元素序列的长度 ...
随机推荐
- HttpClient请求接口实例demo
HttpClient是Apache中的一个开源的项目.它实现了HTTP标准中Client端的所有功能,使用它能够很容易地进行HTTP信息的传输.HttpClient的主要功能: 实现了所有 HTTP ...
- 程序禁止在 VMware 虚拟机中运行的解决办法
本帖最后由 ibq00 于 2018-12-22 18:54 编辑虚拟机里面不能开游戏!提示这个对话框!Sorry, this application cannot run under a Virtu ...
- 【电脑】电脑windows10添加开机启动项
1.添加开机启动项,首先按组合键"win+R"唤出运行窗口,输入"shell:startup"点击确认,打开启动项文件夹. 2.将需要设置为开机启动的软件快捷方 ...
- vcsa7 页面出现“no healthy upstream”处理过程
一.昨天服务器出现"no healthy upstream"错误无法登录,搜索网络给出一下的思路: 对于"no healthy upstream"的错误提示,可 ...
- elelment中el-cascader怎样自定义显示的lable 与value
1.后端返回的数据类型 2.页面代码 3.重点在于 :props="{ value: 'id',label: 'className',children: 'childNode'}" ...
- #Python #字符画 #灰度图 使用Python绘制字符画及其原理
由于最近身体状况不太好所以更新会有点慢,请大家多多包涵.同时也提醒大家注意保重身体! 前提:默认大家已经正确安装了 Python,且正确将Python配置到了系统Path . 目录 1.字符画的概况 ...
- C# core 流、字节、字符串相互转换
/// <summary> /// 将 Stream 转成 byte[] /// </summary> public byte[] StreamToBytes(Stream s ...
- 关于uniapp
1.unipp中,页面生命周期onShow获取上一个传过来的options值. 答:onShow() { // 获取当前小程序的页面栈 let pages = getCurrentPages(); / ...
- 5ppm高精度自动同步标准化考场时钟系统
自动同步标准化考场时钟系统------专业LED时钟厂家![点击进入] 学校考场医院车站GPS/NTP网络校时之组联网对时精度达5ppm原理剖析. 时间精度是根据各个用户所要求对LE ...
- C#.NET系列●接口抽象类
一.接口基本概念 (1)接口:是把公共方法和属性组合起来,以封装特定功能的一个集合.创建接口时,一般一大写的I开头,接口中的成员都是公有的. 接口定义如下: interface IClown //写一 ...