10340 文本编辑器(vim)

经验值: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;
}
 
相关推荐
枫叶红花11 分钟前
【Linux系统编程】:信号(2)——信号的产生
linux·运维·服务器
_微风轻起22 分钟前
linux下网络编程socket&select&epoll的底层实现原理
linux·网络
WANGWUSAN661 小时前
Python高频写法总结!
java·linux·开发语言·数据库·经验分享·python·编程
Stark、2 小时前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端
新手上路狂踩坑2 小时前
Android Studio的笔记--BusyBox相关
android·linux·笔记·android studio·busybox
fnd_LN3 小时前
Linux文件目录 --- 复制命令CP、递归复制目录、软连接、硬链接
linux·运维·服务器
OopspoO4 小时前
Linux查看键鼠输入
linux
七七powerful4 小时前
ansible play-book玩法
linux·服务器·ansible
晚安,cheems5 小时前
linux的权限
linux·运维·服务器
路溪非溪5 小时前
Linux加载一个应用程序的过程总结
linux·运维·服务器