题目链接: P4008 文本编辑器
大致题意
略
解题思路
Splay
直接考虑到我们需要完成的所有操作
insert
相当于在splay中插入一棵子树
delete
相当于在splay中删除一棵子树
get
相当于在splay输出一棵子树的中序遍历
move
prev
next
都是对于光标的操作, 我们直接维护一个全局变量pos表示光标即可
我们分析后发现Splay可以完美的实现这个题的所有操作要求.
但是这里存在一个细节问题, 即: 我们如何去定义pos. 本文中我的定义是, pos指向我们下一个要输出的字符.
对于insert
, 相当于我们要在pos位置进行插入, 我们要找到序列中pos-1和pos的位置
对于delete
, 相当于我们要删除[pos, pos + n - 1], 我们需要找到pos-1和pos+n的位置.
对于get
, 相当于我们要输出**[pos, pos + n - 1], 同样我们需要找到pos-1和pos+n的位置**.
为了防止我们找查不存在的前驱和后继, 我们同样在Splay中插入两个哨兵节点, 因此注意找查位置时, 需要额外计算左哨兵造成的偏移.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1024 * 1024 * 2;
int pos = 1; //初始光标应该指向1位置, 因为要计算左哨兵.
int w[N]; //存要建树的序列.
struct node {
int s[2], p, v;
int size; //因为要找位置, 所以要维护size信息
void init(int _p, int _v) {
s[0] = s[1] = 0;
p = _p, v = _v, size = 1;
}
}t[N]; int root, ind;
void pushup(int x) { t[x].size = t[t[x].s[0]].size + t[t[x].s[1]].size + 1; }
void rotate(int x) {
int y = t[x].p, z = t[y].p;
int k = t[y].s[1] == x;
t[z].s[t[z].s[1] == y] = x, t[x].p = z;
t[y].s[k] = t[x].s[k ^ 1], t[t[x].s[k ^ 1]].p = y;
t[x].s[k ^ 1] = y, t[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k) {
while (t[x].p != k) {
int y = t[x].p, z = t[y].p;
if (z != k) (t[y].s[1] == x) == (t[z].s[1] == y) ? rotate(y) : rotate(x);
rotate(x);
}
if (!k) root = x;
}
int getk(int k) {
int x = root;
while (x) {
int cou = t[t[x].s[0]].size;
if (cou >= k) x = t[x].s[0];
else if (cou + 1 == k) return x;
else k -= cou + 1, x = t[x].s[1];
}
assert(0); //个人习惯
}
int build(int l, int r, int p) { //建立一棵子树
int mid = l + r >> 1;
int x = ++ind; t[x].init(p, w[mid]);
if (l < mid) t[x].s[0] = build(l, mid - 1, x);
if (r > mid) t[x].s[1] = build(mid + 1, r, x);
pushup(x);
return x;
}
void insert() {
int n; scanf("%d", &n);
int num = 0;
while (num != n) { //注意这个题的输入字符要求, 是计算空格而不计算回车的.
char c = getchar();
if (c < 32 or c > 126) continue;
w[++num] = c;
}
int l = getk(pos), r = getk(pos + 1);
splay(l, 0), splay(r, l);
t[r].s[0] = build(1, n, r);
pushup(r), pushup(l);
}
void del() {
int n; scanf("%d", &n);
int l = getk(pos), r = getk(pos + n + 1);
splay(l, 0), splay(r, l);
t[r].s[0] = 0;
pushup(r), pushup(l);
}
void show(int x) { //输出以x为根节点的子树的中序遍历
if (t[x].s[0]) show(t[x].s[0]);
if (t[x].v != 0x3f3f3f3f) printf("%c", t[x].v); //不输出哨兵
if (t[x].s[1]) show(t[x].s[1]);
}
void get() {
int n; scanf("%d", &n);
int l = getk(pos), r = getk(pos + n + 1);
splay(l, 0), splay(r, l);
show(t[r].s[0]); puts("");
}
void init() { //初始化哨兵
w[1] = 0x3f3f3f3f, w[2] = 0x3f3f3f3f;
root = build(1, 2, root);
}
int main()
{
init();
int n; cin >> n;
rep(i, n) {
char s[20]; scanf("%s", s);
if (*s == 'M') scanf("%d", &pos), ++pos; //计算左哨兵产生的偏移
else if (*s == 'I') insert();
else if (*s == 'D') del();
else if (*s == 'G') get();
else if (*s == 'P') pos--;
else pos++;
}
return 0;
}