【XR-4】文本编辑器
直接做是困难的,不妨依照部分分来思考。
- Subtask 3
首先会进入一个误区:维护修改,通过循环串的性质在 \(\tt KMP\) 自动机上优化遍历。
但可以发现这样很难处理,我们不妨 直接维护 每个位置的答案。
令唯一的模式串长度为 \(d\),\(f_i\) 为文本串 \([\max(i - d + 1, 1), i]\) 与模式串是否匹配。
查询直接求 \([L + d - 1, R]\) 的区间和即可。
考虑一次修改对 \(f\) 的影响,显然仅会修改 \([L, R + d - 1]\) 中的 \(f\)。
并且,我们 直接在序列上观察 可以发现:
修改后的 \(f\) 会从 \(L + d\) 开始呈长度为 \(|t|\) 的周期性变化。
由于将区间修改为周期变化的字符串,那么与从 \(L + d\) 开始与模式串的最长 \(border\) 每隔 \(|t|\) 个位置均相同。
则可知从 \(L + d\) 开始的串在 \(\tt KMP\) 自动机上成周期性的遍历,故 \(f\) 也从此位置开始呈长度为 \(|t|\) 的周期性变化。
这意味着我们只需要在 \(\tt KMP\) 自动机上暴力遍历 \(|t|\) 个节点即可求得 \([L + d, R]\) 这一段在修改后的 \(f\) 序列。
但需要注意的是,此时我们假定可以快速得到修改后的序列 \(S_{1, L + d - 1}\) 在 \(\tt KMP\) 自动机上遍历到的节点。
我们将求解这个节点的做法称为「待解决的问题 \(1\)」。
对此,我们本质上只需要支持:
- 给定 \(l, r\) 和一段序列 \(t\),将 \(l \sim r\) 替换为 \(t\) 反复出现的结果。(若最后一段并非完整周期,则将非最后一段和最后一段看作两个修改)
- 给定 \(l, r\),区间查询序列的和。
这两个操作可以简单的使用线段树维护:
对于每一次修改,我们记录修改的序列元素,前缀和,后缀和,以及整体和。
对于线段树上每个节点,我们维护该区间的和 \(sum\),懒标记(当且仅当这个区间被某次修改覆盖时存在):当前被第 \(t\) 次操作覆盖,左边散块开始于 \(t\) 序列中的 \(l\),右边散块结束与 \(t\) 序列的 \(r\),中间整块的数量 \(num\)。
打懒标记,懒标记下传,\(\tt pushup\) 都是容易的。
由此我们以 \(\mathcal{O(\sum |t| + q \log n)}\) 的优秀复杂度解决了 \([L + d, R]\) 的修改。
考虑完 \([L + d, R]\) 这一段的修改,接下来考虑 \([L, L + d - 1]\) 这一段的修改。
注意到 \(d\) 很小,于是可以在 \(S_{1, L - 1}\) 在 \(\tt KMP\) 自动机上的节点开始往下直接遍历。
一样需要注意的是,此时我们假定可以快速得到 \(S_{1, L - 1}\) 在 \(\tt KMP\) 自动机上的节点。
我们将求解这个节点的做法称为「待解决的问题 \(2\)」。
此时我们惊喜地发现,由于我们往后暴力遍历到了 \(S_{1, L + d - 1}\),由此我们解决了「待解决的问题 \(1\)」。
\([R + 1, R + d - 1]\) 的修改与 \([L, L + d - 1]\) 的修改操作是类似的(有一点差别,请自行解决),因此下面只考虑后者的修改。
但现在存在一个问题,我们可以 \(\mathcal{O(d)}\) 获得 \([L, L + d - 1]\) 修改后的 \(f\) 序列,但若要将其在线段树上修改,复杂度看上去将会是 \(\mathcal{O(d \log n)}\) 的,不太行。
事实上,如果我们直接一次修改暴力遍历线段树至叶子节点,其复杂度其实是 \(\mathcal{O(d + \log n)}\) 的。
我们找到区间 \([L, L + d - 1]\) 在线段树上定位的 \(\log n\) 个区间,这里的复杂度是 \(\mathcal{O(\log n)}\) 的。
而接下来遍历的所有节点,实质上是这 \(\log n\) 个区间下面的所有节点。
又线段树的大小是线性的,因此这部分的节点数为 \(\mathcal{O(d)}\)。
至此,我们花费了 \(\mathcal{O}(\sum |t| + q(\log n + d))\) 的花费将这个问题转化为解决:「待解决的问题 \(2\)」
由一开始的观察可知,\(A\) 序列在 \(\tt KMP\) 自动机上遍历得到的节点序列修改后与 \(f\) 有 一模一样 的周期性。
由此我们使用维护 \(f\) 的方法来维护 \(A\) 序列在 \(\tt KMP\) 自动机上遍历得到的节点序列 \(z\),复杂度与 \(f\) 的维护一致。
至此,我们以 \(\mathcal{O(|\Sigma| \sum|s_i| + \sum |t| + q(\log n + d))}\) 的复杂度解决了这个子问题。
- Subtask 4
同样考虑直接维护每个节点的答案,但由于这里为多模式串,因此需要改变定义。
令 \(f_i\) 为 \(A\) 中以 \(i\) 结尾的子串与所有模式串的匹配次数。
令 \(g_{i, j}\) 为 \(A\) 中以 \(j\) 结尾的子串与长度不超过 \(j\) 的模式串匹配的次数。
初始信息我们直接维护出 \(fail\) 树上每个节点的答案,用 \(A\) 在 \(\tt ACAM\) 上直接遍历并继承 \(fail\) 树上的答案即可。
预处理复杂度是 \(\mathcal{O}(\sum |s|(d + |\Sigma|) + nd)\) 的。
一次查询的答案显然为:
\]
对于前半部分,我们直接暴力,单次复杂度 \(\mathcal{O(d)}\),后半部分我们前缀和查询。故复杂度瓶颈在于预处理。
- Subtask 5 \(\sim\) 7
考虑维护 \(Subtask 4\) 中的两个值,查询也使用同样的方式。
虽然加入了多模式串,但我们发现 \(f\) 修改的周期性依然存在,因此 \(f\) 是容易维护的(节点序列 \(z\) 也可以一样的维护)。
又我们维护了节点序列 \(z\),因此我们在计算 \(g\) 的贡献时可以先取出 \([L, L + d - 1]\) 的节点序列 \(z\),然后直接暴力调用 \(\tt ACAM\) 上预处理的每个节点的答案即可。
复杂度 \(\mathcal{O}(\sum |s|(d + |\Sigma|) + \sum |t| + q(\log n + d))\)。
毒瘤题,代码写了一晚上
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define dep(i, l, r) for (int i = r; i >= l; --i)
const int N = 3e5 + 5;
const int M = 1e6 + 5;
const int K = 60 + 5;
struct tree { int l, r, t, num, sum; } ;
vector <int> U[N], pre[N], suf[N];
// U[i][0] 为第 i 次修改的长度,接下来为修改序列
// pre[i], suf[i] 分别为第 i 次修改序列的前缀 / 后缀和
char s[M], t[M];
int n, m, q, z, l, r, x, opt, ans, totU, a[M], b[M], c[M];
// b 为用于暴力区间线段树修改的中转数组
struct ST {
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)
tree t[M << 2];
void build (int p, int l, int r) {
t[p].t = -1, t[p].num = t[p].sum = 0;
if(l == r) { t[p].sum = a[l]; return ; }
build(ls, l, mid), build(rs, mid + 1, r);
t[p].sum = t[ls].sum + t[rs].sum;
}
int gi (int x, int l, int r, tree k) {
if(x < l) return k.l;
if(x > r) return k.r;
int len = U[k.t][0];
if(x - l + 1 <= len - k.l + 1) x = k.l + x - l;
else x = (x - l - len + k.l - 1) % len + 1;
return x;
}
// 求序列中 x 这个位置在修改序列中的位置
tree Get(int l, int r, int ul, int ur, tree k) {
int len = U[k.t][0], id1, id2, sum, nL, nR;
if(l <= ul) id1 = 0;
else {
if(l - ul + 1 <= len - k.l + 1) id1 = 0;
else id1 = ceil(1.0 * (l - ul - len + k.l) / len);
}
if(r - ul + 1 <= len - k.l + 1) id2 = 0;
else id2 = ceil(1.0 * (r - ul - len + k.l) / len);
nL = gi(l, ul, ur, k), nR = gi(r, ul, ur, k);
sum = pre[k.t][len] * (id2 - id1 - 1);
sum += suf[k.t][nL] + pre[k.t][nR];
return (tree){nL, nR, k.t, id2 - id1 - 1, sum};
}
void down (int p, int l, int r) {
if(t[p].t == -1) return ;
t[ls] = Get(l, mid, l, r, t[p]), t[rs] = Get(mid + 1, r, l, r, t[p]);
t[p].t = -1;
}
void update1 (int p, int l, int r, int x, int y, tree k) {
if(x > y || y < l || x > r) return ;
if(l >= x && r <= y) { t[p] = k; return ; }
down(p, l, r);
if(mid >= x) update1(ls, l, mid, x, min(y, mid), Get(l, mid, x, y, k));
if(mid < y) update1(rs, mid + 1, r, max(x, mid + 1), y, Get(mid + 1, r, x, y, k));
t[p].sum = t[ls].sum + t[rs].sum;
}
// 支持区间覆盖
void update2 (int p, int l, int r, int x, int y) {
if(x > y || y < l || x > r) return ;
if(l == r) { t[p].sum = b[l]; return ; }
down(p, l, r);
if(mid >= x) update2(ls, l, mid, x, y);
if(mid < y) update2(rs, mid + 1, r, x, y);
t[p].sum = t[ls].sum + t[rs].sum;
}
// 支持线段树暴力区间单点修改,中转数组为 b
int query (int p, int l, int r, int x, int y) {
if(x > y || y < l || x > r) return 0;
if(l >= x && r <= y) return t[p].sum;
down(p, l, r);
int ans = 0;
if(mid >= x) ans += query(ls, l, mid, x, y);
if(mid < y) ans += query(rs, mid + 1, r, x, y);
return ans;
}
void Get (int p, int l, int r, int x, int y) {
if(x > y || y < l || x > r) return ;
if(l == r) { c[l] = t[p].sum; return ; }
down(p, l, r);
if(mid >= x) Get(ls, l, mid, x, y);
if(mid < y) Get(rs, mid + 1, r, x, y);
}
// 支持线段树暴力区间取出,中转数组为 c
} T[3];
namespace ACAM {
#define Next(i, u) for (int i = h[u]; i; i = e[i].next)
struct edge { int v, next; } e[N << 1];
int cnt, tot, num, h[N], tr[N], fail[N], g[N][K], ch[N][K];
void reset () {
rep(i, 0, cnt) {
fail[i] = 0;
rep(j, 0, 62) ch[i][j] = g[i][j] = 0;
}
rep(i, 0, 25) tr['a' + i] = ++num;
rep(i, 0, 25) tr['A' + i] = ++num;
rep(i, 0, 9) tr['0' + i] = ++num;
cnt = 0;
}
void insert (int n, char s[]) {
int x = 0;
rep(i, 1, n) {
if(!ch[x][tr[s[i] - 0]]) ch[x][tr[s[i] - 0]] = ++cnt;
x = ch[x][tr[s[i] - 0]];
}
++g[x][n];
}
void add (int u, int v) {
e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
e[++tot].v = u, e[tot].next = h[v], h[v] = tot;
}
void dfs (int u, int fa) {
rep(i, 1, 50) g[u][i] += g[fa][i];
Next(i, u) if(e[i].v != fa) dfs(e[i].v, u);
}
void build () {
queue <int> Q;
rep(i, 1, 62) if(ch[0][i]) Q.push(ch[0][i]);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
rep(i, 1, 62) {
if(ch[u][i]) fail[ch[u][i]] = ch[fail[u]][i], Q.push(ch[u][i]);
else ch[u][i] = ch[fail[u]][i];
}
}
rep(i, 1, cnt) add(fail[i], i);
dfs(0, -1);
rep(i, 1, cnt) rep(j, 1, 50) g[i][j] += g[i][j - 1];
}
}
using namespace ACAM;
void Modify (int o, int l, int r, int m, int *a) {
if(l > r) return ;
++totU, U[totU].push_back(m);
rep(i, 1, m) U[totU].push_back(a[i]);
pre[totU].push_back(0), suf[totU].push_back(0);
rep(i, 1, m) pre[totU].push_back(pre[totU][i - 1] + U[totU][i]);
rep(i, 1, m) suf[totU].push_back(0);
suf[totU][m] = U[totU][m];
dep(i, 1, m - 1) suf[totU][i] = suf[totU][i + 1] + U[totU][i];
T[o].update1(1, 1, n, l, r, (tree){1, (r - l) % m + 1, totU, l == r ? -1 : (int)ceil(1.0 * (r - l - 1) / m), 0});
}
signed main () {
scanf("%lld%lld%lld%s", &n, &m, &q, s + 1);
reset();
rep(i, 1, m) scanf("%s", t + 1), l = strlen(t + 1), insert(l, t);
build();
x = 0;
rep(i, 1, n) x = ch[x][tr[s[i] - 0]], a[i] = g[x][50];
T[0].build(1, 1, n);
x = 0;
rep(i, 1, n) x = ch[x][tr[s[i] - 0]], a[i] = x;
T[1].build(1, 1, n);
rep(i, 1, n) a[i] = s[i];
T[2].build(1, 1, n);
while (q--) {
scanf("%lld%lld%lld", &opt, &l, &r);
if(opt == 2) {
scanf("%s", t + 1), m = strlen(t + 1);
rep(i, 1, m) a[i] = t[i];
Modify(2, l, r, m, a);
int cur = T[1].query(1, 1, n, l - 1, l - 1);
rep(i, l, min(l + 49, r))
cur = ch[cur][tr[t[(i - l) % m + 1] - 0]], b[i] = cur;
T[1].update2(1, 1, n, l, min(l + 49, r));
rep(i, min(l + 49, r) + 1, min(l + 49, r) + m)
cur = ch[cur][tr[t[(i - l) % m + 1] - 0]], a[i - min(l + 49, r)] = cur;
Modify(1, min(l + 49, r) + 1, r, m, a);
cur = T[1].query(1, 1, n, r, r);
T[2].Get(1, 1, n, r + 1, min(r + 49, n));
rep(i, r + 1, min(r + 49, n))
cur = ch[cur][tr[c[i]]], b[i] = cur;
T[1].update2(1, 1, n, r + 1, min(r + 49, n));
// 修改 A 序列对应的 ACAM 上的节点序列
cur = T[1].query(1, 1, n, l - 1, l - 1);
rep(i, l, min(l + 49, r))
cur = ch[cur][tr[t[(i - l) % m + 1] - 0]], b[i] = g[cur][50];
T[0].update2(1, 1, n, l, min(l + 49, r));
rep(i, min(l + 49, r) + 1, min(l + 49, r) + m)
cur = ch[cur][tr[t[(i - l) % m + 1] - 0]], a[i - min(l + 49, r)] = g[cur][50];
Modify(0, min(l + 49, r) + 1, r, m, a);
T[1].Get(1, 1, n, r + 1, min(r + 49, n));
rep(i, r + 1, min(r + 49, n)) b[i] = g[c[i]][50];
T[0].update2(1, 1, n, r + 1, min(r + 49, n));
// 修改 f
}
else {
ans = T[0].query(1, 1, n, l + 50, r);
T[1].Get(1, 1, n, l, min(l + 49, r));
rep(i, l, min(l + 49, r)) ans += g[c[i]][i - l + 1];
printf("%lld\n", ans);
}
}
return 0;
}
【XR-4】文本编辑器的更多相关文章
- bbs项目引入富文本编辑器和处理xss攻击和文章预览
一.富文本编辑上传文章和图片 富文本编辑器我们使用kindeditor,我们首先去官网下载,然后解压,放到我们的static的目录中 然后我们在html中这样使用富文本编辑器 <!DOCTYPE ...
- [bzoj1269]文本编辑器editor [bzoj1500]维修数列
1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2540 Solved: 923 [Submit ...
- 富文本编辑器Simditor的简易使用
最近打算自己做一个博客系统,并不打算使用帝国cms或者wordpress之类的做后台管理!自己处于学习阶段也就想把从前台到后台一起谢了.好了,废话不多说了,先来看看富文本编辑器SimDitor,这里是 ...
- 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范
昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...
- 关于SMARTFORMS文本编辑器出错
最近在做ISH的一个打印功能,SMARTFORM的需求本身很简单,但做起来则一波三折. 使用环境是这样的:Windows 7 64bit + SAP GUI 740 Patch 5 + MS Offi ...
- 基于trie树的具有联想功能的文本编辑器
之前的软件设计与开发实践课程中,自己构思的大作业题目.做的具有核心功能,但是还欠缺边边角角的小功能和持久化数据结构,先放出来,有机会一点点改.github:https://github.com/chu ...
- UEditor百度富文本编辑器--让编辑器自适应宽度的解决方案
UEditor百度富文本编辑器的initialFrameWidth属性,默认值是1000. 不能够自适应屏幕宽度.如图1: 刚开始的时候,我是直接设置initialFrameWidth=null的.效 ...
- [bzoj1269][AHOI2006文本编辑器editor] (splay模版题 or pb_ds [rope]大法)
Description 这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器.你能帮助他吗?为了明确任务目标,可可对“文本编辑器”做了一个抽象的定义: 文本:由0个或 ...
- Bzoj1269 [AHOI2006]文本编辑器editor
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3678 Solved: 1380 Description 这些日子,可可不和卡卡一起玩了,原来可可正 ...
- PHP Ueditor 富文本编辑器
2016年12月11日 08:46:59 星期日 百度的简版富文本编辑器umeditor很久没更新了 全功能版本的配置项跟umeditor还是有区别的, 这里说下ueditor怎么对接到项目中去, 主 ...
随机推荐
- 《从LFS到自己的Linux发行版》系列教程:一步到位体验LFS11.0
目录 前言 第一节:LFS 准备工作 第二节:一步完成你的 LFS11.0 第三节:开启你的 LFS 系统 结语 前言 如果你把从源代码开始编译构建一个操作系统的工作当成厨师做一桌菜的话,Lin ...
- AT-GAN: A Generative Attack Model for Adversarial Transferring on Generative Adversarial Nets
目录 概 主要内容 符号说明 Original Generator Transfer the Generator Wang X., He K., Guo C., Weinberger K., Hopc ...
- linux系统安装java
1.下载Java压缩包 *.gz 2.解压 3.修改Linux配置文件,配置Java环境变量 4.使用命令source /etc/profile让修改生效 转载 https://www.cnblogs ...
- PowerDotNet平台化软件架构设计与实现系列(11):日志平台
所有后端应用几乎都会记录日志,日志系统可以统一抽象出来提供服务. 最近被Log4j2的安全漏洞刷屏了,作为开发人员的我只能咩哈哈几次表示日志处理太难了,只有折腾过的人才知道这里面的艰辛啊. 在实现Po ...
- 2021前端面试css(三)
overflow 原理 块格式化上下文是css可视化渲染的一部分,它是一块区域,规定了内部块盒的渲染方式,以及浮动相互之间的影响关系,当元素设置了overflow 样式且值不为visible时,元素就 ...
- 关于 Spring-WebFlux 的一些想法
本文是本人在知乎提问 spring webflux现在看来是否成功? 下的回答,其他回答也很精彩,如果感兴趣可以查看 现在基于 spring web 的同步微服务有一个非常大的缺陷就是:相对于基于 s ...
- 包含全国所有省份、城市、县的一份json文件
最近做项目时,有个需要全国所有省市信息的数据,于是百度了一下,发现CSDN的很多都需要积分下载,无解!所以自己收集了一份整理了出来. 简单说明一下 1.这是一份json文件,这是因为全国的省市信息一般 ...
- 关于C#的decimal浮点类型转化成字符串时末尾存在多个0
首先,对于浮点类型,double和float存在精度丢失问题,这一点在之前的一篇博文中有提到(C# double类型精度丢失问题),于是,一般时候推荐大家使用decmal,特别是涉及到一些金融计算时, ...
- mysql在Linux下大小写敏感设置
默认情况下,mysql在windows下是不区分大小写的,但是mysql在linux下大小写规则是这样的: 1.数据库名与表名是严格区分大小写的: 2.表的别名是严格区分大小写的: 3.列名与列的别名 ...
- go语言 装饰器模式
package decoratorimport ( "fmt" "reflect")func Decorator(decoPtr, fn interface{} ...