直接做是困难的,不妨依照部分分来思考。

- 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)\) 的。

一次查询的答案显然为:

\[\sum\limits_{i = L} ^ {L + d - 1} g_{i - L + 1, i} + \sum\limits_{i = L + d} ^ R f_i
\]

对于前半部分,我们直接暴力,单次复杂度 \(\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】文本编辑器的更多相关文章

  1. bbs项目引入富文本编辑器和处理xss攻击和文章预览

    一.富文本编辑上传文章和图片 富文本编辑器我们使用kindeditor,我们首先去官网下载,然后解压,放到我们的static的目录中 然后我们在html中这样使用富文本编辑器 <!DOCTYPE ...

  2. [bzoj1269]文本编辑器editor [bzoj1500]维修数列

    1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2540 Solved: 923 [Submit ...

  3. 富文本编辑器Simditor的简易使用

    最近打算自己做一个博客系统,并不打算使用帝国cms或者wordpress之类的做后台管理!自己处于学习阶段也就想把从前台到后台一起谢了.好了,废话不多说了,先来看看富文本编辑器SimDitor,这里是 ...

  4. 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范

    昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...

  5. 关于SMARTFORMS文本编辑器出错

    最近在做ISH的一个打印功能,SMARTFORM的需求本身很简单,但做起来则一波三折. 使用环境是这样的:Windows 7 64bit + SAP GUI 740 Patch 5 + MS Offi ...

  6. 基于trie树的具有联想功能的文本编辑器

    之前的软件设计与开发实践课程中,自己构思的大作业题目.做的具有核心功能,但是还欠缺边边角角的小功能和持久化数据结构,先放出来,有机会一点点改.github:https://github.com/chu ...

  7. UEditor百度富文本编辑器--让编辑器自适应宽度的解决方案

    UEditor百度富文本编辑器的initialFrameWidth属性,默认值是1000. 不能够自适应屏幕宽度.如图1: 刚开始的时候,我是直接设置initialFrameWidth=null的.效 ...

  8. [bzoj1269][AHOI2006文本编辑器editor] (splay模版题 or pb_ds [rope]大法)

    Description 这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器.你能帮助他吗?为了明确任务目标,可可对“文本编辑器”做了一个抽象的定义:   文本:由0个或 ...

  9. Bzoj1269 [AHOI2006]文本编辑器editor

    Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3678  Solved: 1380 Description 这些日子,可可不和卡卡一起玩了,原来可可正 ...

  10. PHP Ueditor 富文本编辑器

    2016年12月11日 08:46:59 星期日 百度的简版富文本编辑器umeditor很久没更新了 全功能版本的配置项跟umeditor还是有区别的, 这里说下ueditor怎么对接到项目中去, 主 ...

随机推荐

  1. 用户线程&&守护线程

         守护线程是为用户线程服务的,当一个程序中的所有用户线程都执行完成之后程序就会结束运行,程序结束运行时不会管守护线程是否正在运行,由此我们可以看出守护线程在 Java 体系中权重是比较低的.当 ...

  2. Chapter 15 Outcome Regression and Propensity Scores

    目录 15.1 Outcome regression 15.2 Propensity scores 15.3 Propensity stratification and standardization ...

  3. python 脚本或者flask 注册成为windows服务

    分享下脚本注册成为win服务 这个借鉴了其他大佬得 具体是谁忘记了有机会推荐 今天也是找了之前得改出来得 首先安装三个模块 最后一个是定时任务如果不需要可以不装 pip install psutil ...

  4. Python DataFrame to_sql方法插入日期或时间类型的数据时 报ORA-01861 文字与字符串不匹配 的解决方法

    业务团队近期提出一个需求: 希望在接口调用之前先批量插入Excel中的数据作为数据预置 这个需求以前已经开发完成 本来以为可以很快调试完毕 没成想遭遇一个难关 DataFrame.to_sql方法在执 ...

  5. Tomcat8/9的catalina.out中文乱码问题解决

    OS: Red Hat Enterprise Linux Server release 7.8 (Maipo) Tomcat: 9 中文显示为???问号 在$CATALINA_HOME/conf下的l ...

  6. python安装第三方库的步骤

    windows下举例:1.下载openpyxl,http://pypi.doubanio.com/simple/openpyxl/2.将下载后的文件解压放到Python文件夹下的Lib文件夹下3.cm ...

  7. vue3.0+vite+ts项目搭建-axios封装(六)

    封装方式一 import axios from 'axios' import qs from 'qs' import { Toast } from 'vant' import Lockr from ' ...

  8. Flowable实战(二)集成Springboot

    1.创建Springboot项目   打开IDEA,通过File -> New -> Project- -> Spring Initializr 创建一个新的Springboot项目 ...

  9. ASCII、Unicode和UTF-8等常见字符编码格式介绍

    信息存储在计算机中是转换成二进制来存储的,二进制的发明据说是来源于中国阴阳八卦.后德国数理哲学大师莱布尼茨是最早接触中华文化的欧洲人之一,从他的传教士朋友鲍威特寄给他的拉丁文译本<易经>中 ...

  10. k8s-pv-pvc

    1. 简介 持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者 使用存储类(Storage Class)来动态供应. 持久卷是集群资源,就像节点也是集群资源 ...