【Codeforces 1037H】Security(SAM & 线段树合并)
Description
给出一个字符串 \(S\)。
给出 \(Q\) 个操作,给出 \(L, R, T\),求字典序最小的 \(S_1\),使得 \(S^\prime\) 为\(S[L..R]\) 的子串,且 \(S^\prime\) 的字典序严格大于 \(T\)。输出这个 \(S^\prime\) ,如果无解输出 -1。
Hint
- \(1\le |S|\le 10^5\)
- \(1\le Q\le 2\times 10^5\)
- \(1\le L\le R\le |S|\)
- \(1\le \sum |T| \le 2\times 10^5\)
Solution
看到各种“子串”,考虑 SAM。
要求“字典序严格大于 \(T\) 的字典序最小子串”,那么有一个 贪心 的方法:找一个 \(T\) 的 前缀,后面加一个字典序稍大的字符。
这样的话直接把 \(T\) 放到 \(S\) 的 SAM 上跑,求出 每一位如果替换掉的话可以换的最小字符 \(\text{dir}\)。没有的话就是 \(-1\)。
然后整出 \(\text{dir}\) 之后,倒着 看看有没有 \(\text{dir}\ne -1\) 的位置,有就换掉这一位然后输出,否则答案就是 -1。
注意答案长度可能会比 \(T\) 大一,所以 \(\text{dir}\) 要算到 \(|T| + 1\) 位。
还有一个问题,就是怎么处理区间限制?
这就需要 \(\text{end-pos}\) 了。我指 处理出整个集合。 可以用树上主席树或线段树合并维护。这样可以快速判断 \(\text{end-pos}\) 中 是否含有某个区间中的值。 这样在用 \(T\) 在 SAM 上跑的时候就可以只走区间中的点,替换字符也可以只换可以到达区间中的点。
时空复杂度 \(O(n\log n)\),这里将 \(\Sigma = 26\) 记为常数。
Code
实现比较复杂,注意细节。
线段树合并。
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Codeforces 1037H Security
*/
#include <iostream>
#include <map>
#include <string>
using namespace std;
const int Len = 1e5 + 5;
namespace segt {
const int S = Len << 6;
int lc[S], rc[S];
int total = 0;
#define mid ((l + r) >> 1)
void insert(int& x, int p, int l = 1, int r = Len) {
if (!x) x = ++total;
if (l == r) return;
if (p <= mid) insert(lc[x], p, l, mid);
else insert(rc[x], p, mid + 1, r);
}
int merge(int x, int y, int l = 1, int r = Len) {
if (l == r || !x || !y) return x | y;
int z = ++total;
lc[z] = merge(lc[x], lc[y], l, mid);
rc[z] = merge(rc[x], rc[y], mid + 1, r);
return z;
}
bool find(int u, int v, int x, int l = 1, int r = Len) {
if (!x) return false;
if (u <= l && r <= v) return true;
if (u <= mid && find(u, v, lc[x], l, mid)) return true;
if (v > mid && find(u, v, rc[x], mid + 1, r)) return true;
return false;
}
};
namespace SAM {
const int T = Len << 1;
struct Node {
map<char, int> ch;
int link, len, eprt;
} t[T];
int total;
int last;
void extend_char(char c) {
int p = last, np = last = ++total;
t[np].len = t[p].len + 1;
for (; p && !t[p].ch[c]; p = t[p].link)
t[p].ch[c] = np;
if (!p) {
t[np].link = 1;
} else {
int q = t[p].ch[c];
if (t[p].len + 1 == t[q].len) {
t[np].link = q;
} else {
int nq = ++total;
t[nq] = t[q], t[nq].len = t[p].len + 1;
t[np].link = t[q].link = nq;
for (; p && t[p].ch[c] == q; p = t[p].link)
t[p].ch[c] = nq;
}
}
segt::insert(t[np].eprt, t[np].len);
}
int b[T], c[T];
void topo_sort() {
for (register int i = 1; i <= total; i++) ++c[t[i].len];
for (register int i = 1; i <= total; i++) c[i] += c[i - 1];
for (register int i = 1; i <= total; i++) b[c[t[i].len]--] = i;
}
void init_end_pos() {
for (register int i = total; i; i--) {
int x = b[i], f = t[x].link;
if (f) t[f].eprt = segt::merge(t[x].eprt, t[f].eprt);
}
}
void init_data(string& s) {
total = last = 1;
for (string::iterator it = s.begin(); it != s.end(); it++)
extend_char(*it);
topo_sort();
init_end_pos();
}
int dir[Len];
string query(int l, int r, string str);
};
string SAM::query(int l, int r, string str) {
int x = 1, y, i;
for (i = 1; ; i++) {
dir[i] = -1;
char c = (i > str.size()) ? 'a' : str[i - 1] + 1;
map<char, int>::iterator it = t[x].ch.lower_bound(c);
for (; it != t[x].ch.end(); it++) {
y = it->second;
if (segt::find(l + i - 1, r, t[y].eprt)) {
dir[i] = it->first;
break;
}
}
c = (i > str.size()) ? 0 : str[i - 1];
y = t[x].ch[c];
if (!y || i == str.size() + 1 || !segt::find(l + i - 1, r, t[y].eprt))
break;
x = y;
}
for (; i && dir[i] == -1; i--);
if (!i) return "-1";
string ret;
for (register int j = 1; j < i; j++)
ret += str[j - 1];
ret += dir[i];
return ret;
}
signed main() {
ios::sync_with_stdio(false);
string str;
cin >> str;
SAM::init_data(str);
int q;
cin >> q;
while (q--) {
int L, R;
cin >> L >> R >> str;
cout << SAM::query(L, R, str) << '\n';
}
return 0;
}
【Codeforces 1037H】Security(SAM & 线段树合并)的更多相关文章
- CF1037H Security——SAM+线段树合并
又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...
- CodeForces - 1037H: Security(SAM+线段树合并)
题意:给定字符串S: Q次询问,每次询问给出(L,R,T),让你在S[L,R]里面找一个字典序最小的子串,其字典序比T大. 没有则输出-1: 思路:比T字典序大,而且要求字典最小,显然就是在T的尾巴 ...
- Codeforces 1276F - Asterisk Substrings(SAM+线段树合并+虚树)
Codeforces 题面传送门 & 洛谷题面传送门 SAM hot tea %%%%%%% 首先我们显然可以将所有能够得到的字符串分成六类:\(\varnothing,\text{*},s, ...
- Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...
- loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增
题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...
- 2019.02.27 bzoj4556: [Tjoi2016&Heoi2016]字符串(二分答案+sam+线段树合并)
传送门 题意:给一个字符串SSS. 有mmm次询问,每次给四个参数a,b,c,da,b,c,da,b,c,d,问s[a...b]s[a...b]s[a...b]的所有子串和s[x...y]s[x... ...
- CF700E:Cool Slogans(SAM,线段树合并)
Description 给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是 ...
随机推荐
- ceph使用memdisk做journal
记得在很久很久以前,ceph当时的版本是有提供使用内存做journal的配置的,当时是使用的tmpfs,但是现在的版本在搜资料的时候,发现关于这个的没怎么找到资料,邮件列表里面有人有提到怎么做,看了下 ...
- 对图片进行Base64转码和解码
Base64代码 base64.c #include <stdlib.h> #include <stdio.h> #include <string.h> #incl ...
- 测试_QTP原理
QTP是基于GUI界面的自动化测试工具,用于系统的功能测试. QTP录制的是鼠标和键盘的消息.QTP录制回放时基于windows操作系统的消息机制.QTP在录制时监听应用程序的消息,监听到之后把消 ...
- LeetCode283移动零问题java高效解法
一.题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 1.必须 ...
- NO.A.0004——Git私有服务器部署/makefile方式/本地与Git服务器代码交换
一.在linux服务器上搭建私有Git服务程序:make编译方式 远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改.GitHub就是一个免费托管开源代码的远程仓库.但是对于某 ...
- 不想错过网课?不妨用Camtasia录制下来!
2020年突发的这场疫情给我们的日常生活与学习带来了一些不便,却也意外的让网课走红了起来.小学.中学.大学都开始通过媒体工具或直播平台开始授课,但网络授课与实际课堂上课还是有区别的,学生们受到环境影响 ...
- FL Studio通道常规设置
每个通道设置窗口都包含声相.音量.音高和混音音轨等.刚学习FL Studio的同学可能对这些旋钮的功能还不是很了解,所以也就直接导致了不能很好的运用.为了帮助同学进一步熟悉这款软件,小编今天将为大家详 ...
- laravel数据填充
post表有这2个字段 填充20条数据, 执行 php artisan tinker 执行预览 factory(App\Post::class,20)->make(); 插入数据库 factor ...
- ucore操作系统学习(五) ucore lab5用户进程管理
1. ucore lab5介绍 ucore在lab4中实现了进程/线程机制,能够创建并进行内核线程的调度.通过上下文的切换令线程分时的获得CPU,使得不同线程能够并发的运行. 在lab5中需要更进一步 ...
- C语言讲义——内存管理
动态分配内存 动态分配内存,在堆(heap)中分配. void *malloc(unsigned int num_bytes); 头文件 stdlib.h或malloc.h 向系统申请分配size个字 ...