经验值:1600
时间限制:1000毫秒
内存限制:512MB
经开区2023年信息学竞赛试题
不许抄袭,一旦发现,直接清空经验!
题目描述 Description
李明正在学习使用文本编辑器软件 Vim。与 Word、VSCode 等常用的键鼠配合使用的文本编辑器不同,Vim 采用了键盘操控+模式切换的设计。初始时用户处于普通模式,此时用户可以通过键盘输入命令执行光标移动等操作,或者输入特定命令进入到编辑模式;而在编辑模式中,用户可以从当前光标位置开始,向文本输入内容,然后按 ESC 键切换回普通模式。
李明学习到了普通模式的如下指令。其中(r, c)为当前光标的位置,即第r行 第c列,Lr表示第r行当前的长度,R为当前文本总行数,数学符号min{x, y}表示x与y的最小值,例如min{2,4} = 2:
|指令按键
(注意大小写) | 操作 |
---|---|
h | 若c > 1,即光标不在最左侧,则将光标左移一格至(r, c − 1) |
j | 若r < R,即光标不位于最后一行,则将光标下移一格至(r + 1, min{c, Lr+1 }) |
k | 若r > 1,即光标不位于第一行,则将光标上移一格至(r − 1, min{c, Lr-1 }) |
l | 若c < Lr ,即光标不在当前行的最右侧,则右移一格至(r, c + 1) |
a | 切换至编辑模式,将输入内容插入到光标当前位置的右侧 |
i | 切换至编辑模式,将输入内容插入到光标当前位置的左侧 |
A | 将光标移至本行最右侧的字符处,然后切换至编辑模式,将输入内容插入到光标当前位置的右侧。等价于 lll...a。 |
I | 将光标移至本行最左侧的字符处,然后切换至编辑模式,将输入内容插入到光标当前位置的左侧。等价于 hhh...i。 |
从编辑模式按 ESC 键切换回普通模式时,光标位置位于编辑模式插入的最后一个字符处(假定每次进入编辑模式后至少向文本中插入一个字符)。初始时,Vim 处于普通模式,光标位置为(1,1)。
假设当前文本如下,光标位置用粗斜体+下划线+红色标注。李明的键盘输入序列为 jlliHello^kIWorld^,其中^用于表示 ESC 键:
对于李明的键盘操作,jll 表示下移一格再右移两格,光标按 A→H→I→I 的顺序移动(其中第二次右移因为触及本行末尾无事发生)。此时光标位置为(2,2),即第 2 行第 2 列。Vim 编辑器界面如下:
之后 iHello^表示从当前光标位置进入编辑模式,将插入的内容 Hello 置于此时光标位置 I 的左侧,然后按 ESC 键返回普通模式。此时光标位置为(2,6),指向此次插入内容 Hello 的最后一个字符 o,Vim 编辑器界面如下。另外需注意,指令按键(例如本段示例中的小写字母 l)仅在普通模式下才表示编辑器控制指令,在编辑模式中则作为文本内容输入:
然后按 k 键将光标移至上一行。由于上一行的长度小于当前光标所在的列数,因此光标被移到了上一行的末尾。此时光标位置为(1,3):
最后一段 IWorld^表示,按 I 键将光标移至本行最左侧的字母 A 处,进入编辑模式并将文本 World 插入到字母 A 的左端,然后按 ESC 退出编辑模式,光标停在插入的最后一个字母 d 处,坐标为(1,5)。
已知文本编辑器的初始内容,以及李明的键盘操作序列,你需要输出操作结束后,Vim 编辑器中的文本内容,以及最终光标所在的位置。
输入描述 Input Description
第一行是一个正整数N,表示初始时编辑器中文本的行数;之后N行,每行一个仅包含大小写字母的字符串,表示改行初始时的内容;之后一行为一个仅包含大小写字母和^的字符串,表示李明的键盘操作序列。输入保证操作序列结束后 Vim 处于普通模式,每次进入编辑模式时至少插入一个字符。
输出描述 Output Description
共N + 1行。前N行依次为操作结束后文本编辑器中每一行的内容,最后一行为两个正整数r, c,表示操作结束后光标的位置。
样例输入 Sample Input
样例一: 3 ABC HI OPQRS jlliHello^kIWorld^ 样例二: 3 ABC HI OPQRS jlliHello^kIWorld^jAXyz^jhhaUvw^
样例输出 Sample Output
样例一: WorldABC HHelloI OPQRS 1 5 样例二: WorldABC HHelloIXyz OPQUvwRS 3 6
数据范围及提示 Data Size & Hint
初始时第i行的长度为Li,操作序列的总长度为Lc。所有数据均满足: 1 ≤ N ≤ 100, 1 ≤ Li ≤ 100, 1 ≤ Lc ≤ 10′000
测试数据的分布如下:
占比 | 附加限制 |
---|---|
40% | 普通模式不含 a,i,A,I 指令 |
30% | 普通模式不含 a,i,I 指令 |
20% | 普通模式不含 a,i 指令 |
10% | 无 |
提示与说明:
注意区分数字 1、小写字母 l 与大写字母 I。本题中光标右移指令为小写字母 l,行首插入的指令为大写字母 I。
本题目的细节(例如光标的移动策略)与现实中的 Vim 编辑器存在差异,作答时以本题的描述为准。
cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// Vim编辑器类
class VimEditor {
private:
vector<string> text; // 文本内容
pair<int, int> cursor; // 光标位置
public:
VimEditor(vector<string> initial_text) {
text = initial_text;
cursor = make_pair(0, 0);
}
void executeCommand(char command) {
int r = cursor.first, c = cursor.second;
int R = text.size();
int Lr = text[r].length();
if (command == 'h') {
if (c > 0) {
cursor.second = c - 1;
}
} else if (command == 'j') {
if (r < R - 1) {
cursor.first = r + 1;
cursor.second = min(c, (int)text[r + 1].length());
}
} else if (command == 'k') {
if (r > 0) {
cursor.first = r - 1;
cursor.second = min(c, (int)text[r - 1].length());
}
} else if (command == 'l') {
if (c < Lr - 1) {
cursor.second = c + 1;
}
} else if (command == 'a') {
text[r].insert(c + 1, 1, ' ');
cursor.second = c + 1;
} else if (command == 'i') {
text[r].insert(c, 1, ' ');
cursor.second = c;
} else if (command == 'A') {
cursor.second = Lr - 1;
text[r].insert(Lr, 1, ' ');
cursor.second = Lr;
} else if (command == 'I') {
cursor.second = 0;
text[r].insert(0, 1, ' ');
}
}
void processInput(string input) {
for (char command : input) {
if (command == '^') {
// 切换到普通模式
continue;
}
executeCommand(command);
}
}
void printEditorState() {
for (string line : text) {
cout << line << endl;
}
cout << cursor.first + 1 << " " << cursor.second + 1 << endl;
}
};
int main() {
int N;
cin >> N;
cin.ignore(); // 忽略换行符
vector<string> initial_text(N);
for (int i = 0; i < N; i++) {
getline(cin, initial_text[i]);
}
string input;
getline(cin, input);
VimEditor editor(initial_text);
editor.processInput(input);
editor.printEditorState();
return 0;
}