富文本编辑器中空格转化为a_文本编辑器题解

文本编辑器-题解

内存限制: 256 Mb时间限制: 1000 ms

题目背景

文本编辑器是一种常见的软件,开发高效的文本编辑器是一件相当困难的事情。解决本题需求的数据结构被称为 Gap buffer,是一种非常短小精悍的数据结构。

题目描述

文本编辑器用于编辑文本。开始时,编辑器里记录的文本是空的,为了定位操作的位置,编辑器还提供了一个光标。光标是指向文本的字符间隙的一个标记。

给定 mm 条修改操作,请模拟编辑器在这些操作下的行为,最后输出编辑器所记录的文本内容:

  • 前进操作:编辑器将光标向右移动一格,如果光标已经在最右端,则忽略这步操作;

  • 后退操作:编辑器将光标向左移动一格,如果光标已经在最左端,则忽略这步操作;

  • 插入操作:该操作还需要提供一个字符参数 ch,编辑器在光标的左侧插入该字符;

  • 删除操作:文本编辑器将删去光标右侧的字符,若光标右侧没有字符,则忽略这步操作。

输入格式 第一行:单个整数 m;第二行到第 m+1 行:每行表示一个操作;

前进操作仅有一个字母 f。后退操作仅有一个字母 b;删除操作仅有一个字母 d;插入操作以字母 i 开头,后接一个字母,保证该字母是一个大写的英文字母, i 和该字母中间用一个空格分隔。输出格式 单个字符串:表示编辑器最后所记录的文本内容。

数据范围

  • 对于 30% 的数据,l≤m≤500;

  • 对于 60% 的数据,1≤m≤50000;

  • 对于 %100% 的数据,1≤m≤500000。

样例数据 输入:

5
i H
i E
i L
i L
i O

输出:

HELLO

输入:

6
f
b
i A
b
i B
d

输出:

B

说明: 第一步的f和第二步的b都没有作用,接下来,每步结束后文本和光标的效果为:A| |A B|A B|

分析

在不考虑时间的情况下: 字符串操作, 用字符数组来做, 删除和插入会设计大量的数据的移动, 容易出错, 可以使用string,它支持插入 删除 拼接更方便些. 不过, 还是练习下数组吧.

  • 需要pos表示当前光标位置,还有end表示字符数组的结尾.

  • 光标和字符的下标都是pos点, 那么删除和插入的左右两边怎么区分?

    • 一个判断方法, 光标在pos点,pos点的字符是否可以删除,可以删除的话, 则光标表示pos点字符的左边.

    • 考虑到初始状态, 一个都没输入时, pos 和 end都是 0, 这时pos指向的是待插入字符的位置. 这时pos == end, 表示pos和end之间为空, 删除时,也没有字符可以删除. 插入时, 直接在pos点插入字符.

  • 所以pos最小值和初始值都为0, 表示待插入字符的位置.如果已经有有效字符, 则字符需要后移一位. 删除时, 把pos点的字符删掉, 后面的字符前移一位.

  • pos最大值为end

  • end表示字符结束表示为'\0', 其值等于长度.

这里因为没有说明换行符是\r\n, 还是\n, 所以没敢用getchar()

#include 
using namespace std;

const int MAXN = 500005;
char a[MAXN], op[2], ch[2];
int n, pos, end;

int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%s", op);
switch(op[0]) {
case 'f':
if(pos < end)
pos++;
break;

case 'b':
if(pos > 0)
pos--;
break;

case 'd':
for(int j = pos; j < end - 1; j++)
a[j] = a[j+1];
if(end > pos)
end--;
break;

case 'i':
scanf("%s", ch);
for(int j = end - 1; j >= pos; j--)
a[j+1] = a[j];
a[pos] = ch[0];
pos++;
end++;
break;
}
}

for(int i = 0; i < end; i++)
printf("%c", a[i]);
printf("\n");
return 0;
}

string

C++ 标准库提供了 string 类类型 String类:按照类的方式进行动态管理字符串

底层:是一种顺序表的结构,元素是char类型的字符

访问
str1[i];
长度
str1.length();
str1.size();
insert函数方法:
str.insert(2,3,'A')——在str下标为2的位置添加 3个 字符'A'
str.insert(2,"ABC")——在str下标为2的位置添加 字符串 "ABC"
str.insert(2,"ABC",1)——在str下标为2的位置添加 字符串 "ABC" 中 1个 字符
erase(下标)
str.erase(2)——删除 下标2 的位置开始,之后的全删除
str.erase(2,1)——删除 下标2 的位置开始,之后的 1个 删除

其他函数请参看以前讲义.

cin >> char 会自动过滤空白字符.

#include 
using namespace std;

int n, pos;
string a;
char op, ch[2];

int main() {
ios::sync_with_stdio(false);

cin >> n;
for(int i = 1; i <= n; i++) {
cin >> op;
switch(op) {
case 'f':
if (pos < a.size())
pos++;
break;
case 'b':
if (pos > 0)
pos--;
break;
case 'i':
cin >> ch;
a.insert(pos, ch, 1);
pos++;
break;
case 'd':
a.erase(pos, 1);
break;
}
}

cout << a;

return 0;
}

考虑到时间

  • 对于 %100% 的数据,1≤m≤500000。比较坏的一种情况是 200000个插入, 200000个后退,光标回到0点, 然后100000个插入操作. 那么每次都需要200000+的字符向后移动一位.需要操作200000*100000个后移操作. 这样会超时. 所以可以用链表操作可以避免这个问题.

STL list

list 由双向链表(doubly linked list)实现而成,元素也存放在堆中,每个元素都是放在一块内存中,他的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随机存取变得非常没有效率,因此它没有提供 [] 操作符的重载。但是由于链表的特点,它可以很有效率的支持任意地方的插入和删除操作。

头文件

#include

定义
list<int> a; // 定义一个int类型的列表a

基本操作

大小
  • 容器大小:lst.size();

  • 容器最大容量:lst.max_size();

  • 容器判空:lst.empty();

添加函数
  • 头部添加元素:lst.push_front(const T& x);

  • 末尾添加元素:lst.push_back(const T& x);

  • 任意位置插入一个元素:lst.insert(iterator it, const T& x);

list<int> lst;
// 头部增加元素
lst.push_front(4);

// 末尾添加元素
lst.push_back(5);

// 任意位置插入一个元素
list<int>::iterator it = lst.begin();
lst.insert(it, 2);
删除函数
  • 头部删除元素:lst.pop_front();

  • 末尾删除元素:lst.pop_back();

  • 任意位置删除一个元素:lst.erase(iterator it);

  • 清空所有元素:lst.clear();

迭代器
  • 开始迭代器指针:lst.begin();

  • 末尾迭代器指针:lst.end();

  • 反向迭代器指针,指向最后一个元素:lst.rbegin();

  • 反向迭代器指针,指向第一个元素的前一个元素:lst.rend();

  • 遍历元素

list::iterator it;for (it = lst.begin(); it != lst.end(); it++)
cout << *it << endl;
#include 
using namespace std;

int n;
char op, ch;
list<char> a;
list<char>::iterator it;

int main() {
ios::sync_with_stdio(false);

cin >> n;
it = a.begin();
for(int i = 1; i <= n; i++) {
cin >> op;
switch(op) {
case 'f':
if (it != a.end())
it++;
break;
case 'b':
if (it != a.begin())
it--;
break;
case 'i':
cin >> ch;
a.insert(it, ch);
break;
case 'd':
if(it != a.end())
it = a.erase(it);
break;
}
}

for (it = a.begin(); it != a.end(); it++) {
cout << *it;
}

return 0;
}


数组实现list

使用int next[N]数组指向下一个字符. 将所有插入的数据一次放入 char a[N]中.

插入时:

cin >> a[i];
next[i] = next[pos];
next[pos] = i;

删除时:

next[pos] = next[next[pos];

代码略.

参考题目:

UVA11988 破损的键盘 Broken Keyboard (a.k.a. Beiju Text)

难度提升:

这种题目可以变的比较难 比如:

P4008 [NOI2003]文本编辑器

云帆优培公众号:

0ffd464f83e9dd131b2b4965a18a3c12.png

云帆优培老师联系方式:

云帆老师

微信:

36868277a24fde7835a2c5da2c4a678c.png

云帆优培介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值