注:本题有较多的部分分,请参看数据规模部分。
题目描述
记事本是 Windows 平台下一款经典的文本编辑器,其存储文件的扩展名为 .txt
,文件属性没有任何格式标签或者风格,所以相当适合在 DOS 环境中编辑。
在本题中,可能会用到的按键如下图所示:
光标移动
光标表示当前要进行输入等操作的位置,在本题中,我们假设所有字符都是等宽的。
光标的位置可以用行列坐标来描述,光标所在的行和列均从1开始,例如:
以下操作可以进行光标的移动,使用 MOVE <comd>
输入相关的操作,其中 <comd>
表示指令,可以使用以下字符串代替:
Home
:把光标移动到当前行的开头。End
:把光标移动到当前行的末尾。Up
:光标移动到上一行的相同列。- 若当前为第一行,则不进行任何操作。
- 若上一行的列数小于当前光标的列数,则将光标移动到上一行的末尾。
Down
:光标移动到下一行的相同列。- 若当前为最后一行,则不进行任何操作。
- 若下一行的列数小于当前光标的列数,则将光标移动到下一行的末尾。
Left
:光标左移一位。- 若当前光标位于记事本开始,则不进行任何操作。
- 若当前光标处于某一行的开头,则将光标移动到上一行的末尾。
Right
:光标右移一位。- 若当前光标位于记事本末尾,则不进行任何操作。
- 若当前光标处于某一行的末尾,则将光标移动到下一行的开头。
输入
以下操作可以在光标后进行输入,使用 INSERT <comd>
输入相关的操作,其中 <comd>
表示指令,可以使用以下字符串代替:
-
Char <char>
:输入一个字符,其中<char>
是输入的字符。<char>
可能是一下字符中的任意一个:
注:下列字符中不包含空格与换行符。
`1234567890-=~!@#$%^&*()_+qwertyuiop[]\QWERTYUIOP{}|asdfghjkl;'ASDFGHJKL:"zxcvbnm,./ZXCVBNM<>?
- 例如:
INSERT Char a
表示在当前光标后插入a
字符。
-
Enter
:输入换行符,并进行换行。 -
Space
:输入空格。 -
Paste
:在当前光标后,插入粘贴板中的内容,若粘贴板中无内容,则忽略当前操作。
删除
以下操作可以删除记事本中的内容,使用 REMOVE <comd>
输入相关的操作,其中 <comd>
表示指令,可以使用以下字符串代替:
Del
:删除当前光标位置之后的一个字符。- 若该字符为换行符,则当前行与下一行合并为一行。
- 若当前光标在文件末尾,则忽略当前操作。
Backspace
:删除当前光标位置之前的一个字符。- 若该字符为换行符,则当前行与上一行合并为一行。
- 若当前光标在文件开头,则忽略当前操作。
粘滞功能(分数占比 24分)
输入SHIFT
指令,可以启动或关闭粘滞功能。
-
开始时粘滞功能默认为关闭状态,之后每次点击:
- 若当前为启动状态,则关闭;
- 若当前为关闭状态,则启动。
-
粘滞功能启动时,记录当前的光标位置为 记录点。
-
粘滞功能关闭时,若此时的光标位置与 记录点 的位置不同,则进入选中状态。
-
粘滞功能启动后,直到功能关闭前,不会对记事本进行除光标移动外的任何操作。
当进入选中状态后,通过记录点与当前光标,可以唯一的确定一段内容,现令记录点与光标之间的所有字符(包括换行符)为 选中字段。
例如,记录点位于第1行第2列,光标位于第2行第4列时,选中字段如下图所示:
当前 处于选中状态 时,对于不同的情况,需要按照序号依次执行以下操作:
-
若进行光标移动:
- 退出选中状态;
- 尝试进行光标的移动(无论光标最终是否移动,都会退出选中状态)。
-
若进行输入:
- 将选中内容替换为输入内容;
- 退出选中状态。
-
若进行删除:
- 删除当前选中内容;
- 退出选中状态。
-
若再次启动粘滞功能:退出选中状态,但保留上一次选中字段的 记录点 作为当前记录点。
-
若进行查找,字数统计,复制,打印操作,则在操作后仍然保持选中状态。
查找
输入FIND <word>
指令,进行字符串查找,其中 <word>
为输入的要查找的字符串,该字符串中不包含空格与换行符。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
- 若当前处于选中状态:查找输入字符串在选中字段中的出现次数并输出。
- 否则:查找输入字符串在当前记事本中的出现次数并输出。
例如:当前没有选中的内容,且记事本中的内容为 ababa
,若执行 FIND aba
,则应当输出2,分别在第1列与第3列出现过。
字数统计
输入COUNT
指令,进行字数统计。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
- 若当前处于选中状态:输出当前选中字段中的可见字符(不包括空格与换行符)的数量。
- 否则:输出当前文档中可见字符(不包括空格与换行符)的数量。
复制
输入COPY
指令,进行复制操作。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
- 若当前处于选中状态:复制选中字段到粘贴板;
- 否则,
- 若当前行不为空:复制当前行的内容(不包括换行符)到粘贴板;
- 否则:忽略当前操作。
打印
输入PRINT
指令,输出当前的记事本中的全部内容,并在之后输出一个换行符。
输入格式
输入包含 行。
第一行包含一个整数 ,表示接下来指令的数量。
接下来 行,每行一条指令,格式形如题目描述中的叙述。
输出格式
对于需要输出的指令,进行相应的输出。
若为 FIND
与 COUNT
操作,输出一行表示相应的数字。
若为 PRINT
操作,则输出若干行,表示记事本的当前内容,并在之后输出一个换行。
请注意:所有的输出不要有多余的空格。
测试样例
样例输入
20
INSERT Char #
INSERT Enter
INSERT Char C
INSERT Enter
INSERT Space
INSERT Char _
INSERT Char _
PRINT
INSERT Char >
INSERT Enter
INSERT Char h
INSERT Char h
INSERT Char h
INSERT Enter
PRINT
COUNT
FIND __
REMOVE Del
REMOVE Backspace
PRINT
样例输出
#
C
__
#
C
__>
hhh
8
1
#
C
__>
hhh
数据规模
对于 的测试数据,。
不同测试点所包含的功能不同,其具体情况如下表所示。
#include <bits/stdc++.h>
using namespace std;
enum comd {
home,
en, // end
le, // left
ri, // right
up,
down,
del,
backspace
};
string txt, copyTxt;
int remember = -1, now = -1; //选区,string的下标,当执行shift就更新remember=now
bool selected = false, shifted = false, haveCopy = false;
int move(comd com) {
selected = false;
if (com == le) {
if (now == -1) //如果光标在开头,则不能左移
return 0;
now--;
return 1;
} else if (com == ri) {
if (now == txt.size() - 1) //如果在文档末尾
return 0;
now++;
return 1;
} else if (com == home) { //前往上一行换行符后
int t = 0;
while (now > -1 && txt[now] != '\n')
now--,t++;
return t;
} else if (com == en) { //前往本行换行符前
int t = 0;
while (now + 1 != txt.size() && txt[now + 1] != '\n')
now++,t++;
return t;
} else if (com == up) {
auto col = move(home);
move(le);
move(home);
while (col > 0 && now + 1 != txt.size() && txt[now + 1] != '\n')
move(ri),col--;
} else if (com == down) {
auto col = move(home);
move(en);
if (!move(ri))
move(home);
while (col > 0 && now + 1 != txt.size() && txt[now + 1] != '\n')
move(ri),col--;
}
return 0;
}
//插入字符串,是否从剪贴板插入由主函数决定是否c=copyTxt
void insert(string c) {
if (!selected) {
txt.insert(now + 1, c);
now += c.size();
} else { //如果选中了
int ks = min(now, remember) + 1, js = max(now, remember); // ks是选区第一个字符的下标,js是选区最后一个字符的下标
txt.replace(ks, js - ks + 1, c); //替换选区内容
selected = false;
now = ks + c.size() - 1;//更新光标为替换后的字符串的最后一个字符的下标
}
}
void remove(comd com) {
if (!selected) {
if (com == del) {
if (now + 1 != txt.size())
txt.erase(now + 1, 1);
} else if (com == backspace)
if (now != -1)
txt.erase(now--, 1);
} else {
int ks = min(now, remember) + 1, js = max(now, remember); // ks是选区第一个字符的下标,js是选区最后一个字符的下标
txt.erase(ks, js - ks + 1);
selected = false;
now = ks - 1;
}
}
void find(string word) {
int ks = 0, js = txt.size() - 1, cnt = 0, len = word.size();
if (selected) {
ks = min(now, remember) + 1;
js = max(now, remember);
}
while (ks + len-1 <= js)//有可能选区只有1个字符,然后find也是1个字符
if (txt.substr(ks++, len) == word)
cnt++;
printf("%d\n", cnt);
}
void count() {
int ks = 0, js = txt.size() - 1, cnt = 0;
if (selected) {
ks = min(now, remember) + 1;
js = max(now, remember);
}
while (ks <= js) {
if (txt[ks] != '\n' && txt[ks] != ' ')
cnt++;
ks++;
}
printf("%d\n", cnt);
}
void copy() {
if (selected) {
copyTxt = txt.substr(min(now, remember) + 1, abs(now - remember));
haveCopy = true;
} else {
int saveNow = now;
int ks = move(home), num = move(en);
now = saveNow;
if (num == 0)
return;
copyTxt = txt.substr(now - ks + 1, num);
haveCopy = true;
}
}
int main() {
int n;
string op, wo, tmp;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
cin >> op;
if (op == "MOVE") {
cin >> wo;
if (wo == "Home") {
move(home);
} else if (wo == "End") {
move(en);
} else if (wo == "Up") {
move(up);
} else if (wo == "Down") {
move(down);
} else if (wo == "Left") {
move(le);
} else if (wo == "Right") {
move(ri);
}
} else if (op == "INSERT") {
cin >> wo;
if (wo == "Char") {
cin >> tmp;
insert(tmp);
} else if (wo == "Enter") {
insert("\n");
} else if (wo == "Space") {
insert(" ");
} else if (wo == "Paste") {
if (haveCopy)
insert(copyTxt);
}
} else if (op == "REMOVE") {
cin >> wo;
if (wo == "Del") {
remove(del);
} else
remove(backspace);
} else if (op == "SHIFT") {
if (shifted) {
shifted = false;
if (remember != now)
selected = true;
} else {
shifted = true;
if (selected) {
selected = false;
} else
remember = now;
}
} else if (op == "FIND") {
cin >> wo;
find(wo);
} else if (op == "COUNT") {
count();
} else if (op == "COPY") {
copy();
} else if (op == "PRINT") {
cout << txt << '\n';
}
}
return 0;
}