5月模拟-记事本
山东大学计算机科学与技术学院程序设计思维与实践作业
山大程序设计思维与实践
sdu程序设计思维与实践
山东大学程序设计思维与实践
相关资料:GitHub
记事本
注:本题有较多的部分分,请参看数据规模部分。
题目描述
记事本是 Windows 平台下一款经典的文本编辑器,其存储文件的扩展名为 .txt,文件属性没有任何格式标签或者风格,所以相当适合在 DOS 环境中编辑。
在本题中,可能会用到的按键如下图所示:
光标移动
光标表示当前要进行输入等操作的位置,在本题中,我们假设所有字符都是等宽的。
光标的位置可以用行列坐标来描述,光标所在的行和列均从 1 开始,例如:
以下操作可以进行光标的移动,使用 MOVE 输入相关的操作,其中 表示指令,可以使用以下字符串代替:
Home:把光标移动到当前行的开头。
End:把光标移动到当前行的末尾。
Up:光标移动到上一行的相同列。
若当前为第一行,则不进行任何操作。
若上一行的列数小于当前光标的列数,则将光标移动到上一行的末尾。
Down:光标移动到下一行的相同列。
若当前为最后一行,则不进行任何操作。
若下一行的列数小于当前光标的列数,则将光标移动到下一行的末尾。
Left:光标左移一位。
若当前光标位于记事本开始,则不进行任何操作。
若当前光标处于某一行的开头,则将光标移动到上一行的末尾。
Right:光标右移一位。
若当前光标位于记事本末尾,则不进行任何操作。
若当前光标处于某一行的末尾,则将光标移动到下一行的开头。
输入
以下操作可以在光标后进行输入,使用 INSERT 输入相关的操作,其中 表示指令,可以使用以下字符串代替:
Char :输入一个字符,其中 是输入的字符。
可能是一下字符中的任意一个:
注:下列字符中不包含空格与换行符。
`1234567890-=~!@#$%^&*()_+qwertyuiop[]\QWERTYUIOP{}|asdfghjkl;'ASDFGHJKL:"zxcvbnm,./ZXCVBNM<>?
例如:INSERT Char a 表示在当前光标后插入 a 字符。
Enter:输入换行符,并进行换行。
Space:输入空格。
Paste:在当前光标后,插入粘贴板中的内容,若粘贴板中无内容,则忽略当前操作。
删除
以下操作可以删除记事本中的内容,使用 REMOVE 输入相关的操作,其中 表示指令,可以使用以下字符串代替:
Del:删除当前光标位置之后的一个字符。
若该字符为换行符,则当前行与下一行合并为一行。
若当前光标在文件末尾,则忽略当前操作。
Backspace:删除当前光标位置之前的一个字符。
若该字符为换行符,则当前行与上一行合并为一行。
若当前光标在文件开头,则忽略当前操作。
粘滞功能(分数占比 24 分)
输入 SHIFT 指令,可以启动或关闭粘滞功能。
开始时粘滞功能默认为关闭状态,之后每次点击:
若当前为启动状态,则关闭;
若当前为关闭状态,则启动。
粘滞功能启动时,记录当前的光标位置为 记录点。
粘滞功能关闭时,若此时的光标位置与 记录点 的位置不同,则进入选中状态。
粘滞功能启动后,直到功能关闭前,不会对记事本进行除光标移动外的任何操作。
当进入选中状态后,通过记录点与当前光标,可以唯一的确定一段内容,现令记录点与光标之间的所有字符(包括换行符)为 选中字段。
例如,记录点位于第 1 行第 2 列,光标位于第 2 行第 4 列时,选中字段如下图所示:
当前 处于选中状态 时,对于不同的情况,需要按照序号依次执行以下操作:
若进行光标移动:
退出选中状态;
尝试进行光标的移动(无论光标最终是否移动,都会退出选中状态)。
若进行输入:
将选中内容替换为输入内容;
退出选中状态。
若进行删除:
删除当前选中内容;
退出选中状态。
若再次启动粘滞功能:退出选中状态,但保留上一次选中字段的 记录点 作为当前记录点。
若进行查找,字数统计,复制,打印操作,则在操作后仍然保持选中状态。
查找
输入 FIND 指令,进行字符串查找,其中 为输入的要查找的字符串,该字符串中不包含空格与换行符。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
若当前处于选中状态:查找输入字符串在选中字段中的出现次数并输出。
否则:查找输入字符串在当前记事本中的出现次数并输出。
例如:当前没有选中的内容,且记事本中的内容为 ababa,若执行 FIND aba,则应当输出 2,分别在第 1 列与第 3 列出现过。
字数统计
输入 COUNT 指令,进行字数统计。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
若当前处于选中状态:输出当前选中字段中的可见字符(不包括空格与换行符)的数量。
否则:输出当前文档中可见字符(不包括空格与换行符)的数量。
复制
输入 COPY 指令,进行复制操作。
执行该指令时,要根据当前是否处于选中状态做不同的处理:
若当前处于选中状态:复制选中字段到粘贴板;
否则,
若当前行不为空:复制当前行的内容(不包括换行符)到粘贴板;
否则:忽略当前操作。
打印
输入 PRINT 指令,输出当前的记事本中的全部内容,并在之后输出一个换行符。
输入格式
输入包含 n+1 行。
第一行包含一个整数 n,表示接下来指令的数量。
接下来 n 行,每行一条指令,格式形如题目描述中的叙述。
输出格式
对于需要输出的指令,进行相应的输出。
若为 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
C
__
C
__>
hhh
8
1
C
__>
hhh
#include <bits/stdc++.h>
using namespace std;
vector<string> text;
deque<string> pst;
pair<int, int> cursor, mark, st, ed;
bool chs;
void delSE() {
int delst = INT_MAX, deled = INT_MIN;
for (int i = st.first; i <= ed.first; i++) {
if (i < ed.first) {
if (i == st.first) {
text[i - 1].erase(st.second - 1);
} else {
delst = min(delst, i);
deled = max(deled, i);
}
} else {
if (i == st.first) {
text[i - 1].erase(st.second - 1, ed.second - st.second);
} else {
text[i - 1].erase(0, ed.second - 1);
text[st.first - 1].append(text[i - 1]);
delst = min(delst, i);
deled = max(deled, i);
}
}
}
if (delst != INT_MAX && deled != INT_MIN) {
text.erase(text.begin() + delst - 1, text.begin() + deled);
}
cursor = st;
}
vector<int> prefix_function(string s) { //字符串前缀函数
int n = s.length();
vector<int> vec(n);
for (int i = 1; i < n; i++) {
int j = vec[i - 1];
while (j > 0 && s[i] != s[j])
j = vec[j - 1];
if (s[i] == s[j])
j++;
vec[i] = j;
}
return vec;
}
void move(string command) {
if (command == "Home") {
cursor.second = 1;
}
if (command == "End") {
if (chs)
cursor = ed;
cursor.second = text[cursor.first - 1].size() + 1;
}
if (command == "Up") {
if (cursor.first > 1) { //-2是因为下标访问需要-1
cursor.second = text[cursor.first - 2].size() + 1 < cursor.second ? text[cursor.first - 2].size() + 1
: cursor.second;
cursor.first--;
}
}
if (command == "Down") {
if (cursor.first < text.size()) { //没有+1因为下标访问需要-1
cursor.second =
text[cursor.first].size() + 1 < cursor.second ? text[cursor.first].size() + 1 : cursor.second;
cursor.first++;
}
}
if (command == "Left") {
if (cursor.first != 1 || cursor.second != 1) {
if (cursor.second == 1) {
cursor.first--;
cursor.second = text[cursor.first - 1].size() + 1;
} else {
cursor.second--;
}
}
}
if (command == "Right") {
if (cursor.first != text.size() ||
cursor.second != text.back().size() + 1) {
if (cursor.second == text[cursor.first - 1].size() + 1) {
cursor.first++;
cursor.second = 1;
} else {
cursor.second++;
}
}
}
}
void insert(const string &command) {
if (chs) {
delSE();
chs = false;
}
if (command == "Char") {
string tmp;
cin >> tmp;
text[cursor.first - 1].insert(cursor.second - 1, tmp);
cursor.second++;
}
if (command == "Enter") {
string tmp =
text[cursor.first - 1].substr(cursor.second - 1); //获取子串
text[cursor.first - 1].erase(cursor.second - 1); //删除子串
text.insert(text.begin() + cursor.first, tmp);
cursor.first++;
cursor.second = 1;
}
if (command == "Space") {
text[cursor.first - 1].insert(cursor.second - 1, " ");
cursor.second++;
}
if (command == "Paste") {
if (!pst.empty()) {
auto tpst = pst;
pair<int, int> ptr = cursor;
string tmp = text[cursor.first - 1].substr(cursor.second - 1);
if (!tmp.empty()) {
text[cursor.first - 1].erase(text[cursor.first - 1].begin() +
cursor.second - 1, text[cursor.first - 1].end());
}
text[cursor.first - 1].append(pst.front());
pst.pop_front();
while (!pst.empty()) {
text.insert(text.begin() + cursor.first, pst.back());
pst.pop_back();
ptr.first++;
}
ptr.second = text[ptr.first - 1].size() + 1;
text[ptr.first - 1].append(tmp);
cursor = ptr;
pst = tpst;
}
}
}
void remove(const string &command) {
if (chs) {
delSE();
chs = false;
return;
}
if (command == "Del") {
if (cursor.first != text.size() || cursor.second != text.back().size() + 1) { //不在文件末尾
if (cursor.second == text[cursor.first - 1].size() + 1) { //行末
text[cursor.first - 1].append(text[cursor.first]);
text.erase(text.begin() + cursor.first);
} else {
text[cursor.first - 1].erase(text[cursor.first - 1].begin() +
cursor.second - 1);
}
}
}
if (command == "Backspace") {
if (cursor.first != 1 || cursor.second != 1) {
if (cursor.second == 1) {
auto tmp = text[cursor.first - 2].size() + 1;
text[cursor.first - 2].append(text[cursor.first - 1]);
text.erase(text.begin() + cursor.first - 1);
cursor.second = tmp;
cursor.first--;
} else {
text[cursor.first - 1].erase(text[cursor.first - 1].begin() +
cursor.second - 2);
cursor.second--;
}
}
}
}
int Find(string &word) {
if (chs) {
string cmp;
int result = 0;
for (int i = st.first; i <= ed.first; i++) {
if (i < ed.first) {
if (i == st.first) {
cmp = text[i - 1].substr(st.second - 1);
} else {
cmp = text[i - 1];
}
} else {
if (i == st.first) {
cmp = text[i - 1].substr(st.second - 1,
ed.second - st.second);
} else {
cmp = text[i - 1].substr(0, ed.second - 1);
}
}
string tp = word + "\n";
for (char i : cmp) {
tp += i;
auto vec = prefix_function(tp);
if (vec[tp.size() - 1] == word.size())
result++;
}
}
return result;
}
int result = 0;
for (auto & i : text) {
string tp = word + "\n";
for (int j = 0; j < i.size(); j++) {
tp += i[j];
auto vec = prefix_function(tp);
if (vec[tp.size() - 1] == word.size())
result++;
}
}
return result;
}
int countNum(string s) {
int n = 0;
for (auto j: s) {
if (j != ' ')
n++;
}
return n;
}
int count() {
if (chs) {
int n = 0;
for (int i = st.first; i <= ed.first; i++) {
if (i < ed.first) {
if (i == st.first) {
n += countNum(text[i - 1].substr(st.second - 1));
} else {
n += countNum(text[i - 1]);
}
} else {
if (i == st.first) {
n += countNum(text[i - 1].substr(st.second - 1,
ed.second - st.second));
} else {
n += countNum(text[i - 1].substr(0, ed.second - 1));
}
}
}
return n;
}
int n = 0;
for (const auto& i: text) {
n += countNum(i);
}
return n;
}
void copy() {
if (chs) {
pst.clear();
for (int i = st.first; i <= ed.first; i++) {
if (i < ed.first) {
if (i == st.first) {
pst.push_back(text[i - 1].substr(st.second - 1));
} else {
pst.push_back(text[i - 1]);
}
} else {
if (i == st.first) {
pst.push_back(text[i - 1].substr(st.second - 1,
ed.second - st.second));
} else {
pst.push_back(text[i - 1].substr(0, ed.second - 1));
}
}
}
return;
}
if (!text[cursor.first - 1].empty()) {
pst.clear();
pst.push_back(text[cursor.first - 1]);
}
}
void moveInSht(int &i) {
string op;
if (!chs) {
mark = cursor;
}else{
chs = false;
}
while (cin >> op && op != "SHIFT") {
string command;
cin >> command;
move(command);
i++;
}
i++;
if (cursor != mark) {
st = cursor < mark ? cursor : mark;
ed = cursor < mark ? mark : cursor;
chs = true;
}
}
void print() {
for (const auto& i: text)
cout << i << endl;
}
int main() {
int n;
cin >> n;
cursor = {1, 1};
chs = false;
text.push_back("");
for (int i = 1; i <= n; i++) {
string op;
cin >> op;
if (op == "MOVE") {
chs = false;
string command;
cin >> command;
move(command);
continue;
}
if (op == "INSERT") {
string command;
cin >> command;
insert(command);
continue;
}
if (op == "REMOVE") {
string command;
cin >> command;
remove(command);
continue;
}
if (op == "FIND") {
string word;
cin >> word;
cout << Find(word) << endl;
continue;
}
if (op == "COUNT") {
cout << count() << endl;
continue;
}
if (op == "COPY") {
copy();
continue;
}
if (op == "PRINT") {
print();
continue;
}
if (op == "SHIFT") {
moveInSht(i);
continue;
}
}
return 0;
}