P4008 文本编辑器 (Splay)

题目链接: 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;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值