题目描述
记事本是 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
指令,输出当前的记事本中的全部内容,并在之后输出一个换行符。
输入格式
输入包含 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
数据规模
对于 100% 的测试数据,1≤n≤5000。
不同测试点所包含的功能不同,其具体情况如下表所示。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int row, column;
int sym_r, sym_c;//记录点
vector<string> cop(1);
bool flag = false;//选中状态
bool zhan = false;
vector<string> ban;//
int ro, co, sr, sc;
void erase_it()//删除选中内容
{
flag = false;
if (sym_r == row)//同一行
{
cop[row].erase(co, sc - co);
column = co;
}
else //两行以上
{
cop[ro] = cop[ro].substr(0, co);//存前面的
string tem = cop[sr].substr(sc);//后半部分
cop.erase(cop.begin() + sr);
for (int i = ro + 1; i < sr; ++i)
{
cop.erase(cop.begin() + ro + 1);
}
cop[ro] = cop[ro] + tem;
row = ro; column = co;//更新光标
}
}
//光标移动
void mov()
{
if (flag) { flag = false; }//退出选中状态
string comd;
cin >> comd;
if (comd == "Home")//把光标移动到当前行的开头
{
column = 0;
}
else if (comd == "End")//把光标移动到当前行的末尾
{
column = cop[row].size();
}
else if (comd == "Up")//光标移动到上一行的相同列
{
if (row == 0) return;
row--;
if (cop[row].size() < column)
{
column = cop[row].size();
}
}
else if (comd == "Down")//光标移动到下一行的相同列
{
if (row == cop.size() - 1) return;
row++;
if (cop[row].size() < column)
{
column = cop[row].size();
}
}
else if (comd == "Left")//光标左移一位
{
if (row == 0 && column == 0) return;
if (column == 0)//移到上一行末尾
{
row--;
column = cop[row].size();
return;
}
column--;
}
else if(comd=="Right")//光标右移一位
{
if (row == cop.size() - 1 && column == cop[row].size()) return;
if (column == cop[row].size())//移到下一行开头
{
row++;
column = 0;
return;
}
column++;
}
}
//输入
void inser()
{
if (flag) { erase_it(); }
string comd;
cin >> comd;
if (comd == "Char")//输入一个字符
{
string c;
cin >> c;
cop[row].insert(column, c);
column++;//向右移
}
else if (comd == "Enter")//输入换行符,并进行换行
{
string a = cop[row].substr(0, column);//前
string b = cop[row].substr(column);//后
cop[row] = a;
cop.insert(cop.begin() + row + 1, b);
column = 0; row++;
}
else if (comd == "Space")//输入空格
{
cop[row].insert(column, " ");
column++;
}
else if (comd == "Paste")//在当前光标后,插入粘贴板中的内容,若粘贴板中无内容,则忽略当前操作
{
if (ban.size() == 0) return;
if (ban.size() == 1)
{
cop[row].insert(column, ban[0]);
column += ban[0].size();
}
else if (ban.size() == 2)
{
string a = cop[row].substr(0, column) + ban[0];
string b = ban[1] + cop[row].substr(column);
cop[row] = a;
cop.insert(cop.begin() + row + 1, b);
row++; column = ban[1].size();
}
else
{
string a = cop[row].substr(0, column) + ban[0];
string b = ban[ban.size()-1] + cop[row].substr(column);
cop[row] = a;
cop.insert(cop.begin() + row + 1, b);
for (int i = 1; i < ban.size() - 1; ++i)
{
cop.insert(cop.begin() + row + i, ban[i]);//插入到第row+1行前面
}
row += ban.size() - 1;
column = ban[ban.size() - 1].size();
}
}
}
//删除
void remove()
{
if (flag) { erase_it(); return; }
string comd;
cin >> comd;
if (comd == "Del")//删除当前光标位置之后的一个字符
{
if (row == cop.size() - 1 && column == cop[row].size()) return;
else if (column == cop[row].size())//如果为换行符
{
cop[row] = cop[row] + cop[row + 1];
cop.erase(cop.begin() + row + 1, cop.begin() + row + 2);
return;
}
cop[row].erase(column, 1);
}
else//删除当前光标位置之前的一个字符
{
if (row == 0 && column == 0) return;
else if (column == 0)
{
cop[row] = cop[row - 1] + cop[row];
column = cop[row - 1].size();
cop.erase(cop.begin() + row - 1, cop.begin() + row);
row--;
return;
}
cop[row].erase(column - 1, 1);
column--;
}
}
//粘滞功能
void shift()
{
if (zhan)//关闭粘滞功能
{
if (sym_c != column || sym_r != row)//光标位置与记录点位置不同
{
sc = sym_c; sr = sym_r; co = column; ro = row;
if (sr < ro) //保持sr>=ro
{
swap(sr, ro); swap(sc, co);
}
else if (sr == ro && sc < co) { swap(sc, co); }
flag = true;//进入选中状态
}
zhan = false;
}
else//启动粘滞功能
{
if (flag)//
{
flag = false;
}
else
{
sym_c = column; sym_r = row;//更新记录点
}
zhan = true;
}
}
//查找
void find_it()
{
string word;
cin >> word;
int coun = 0;
if(flag)//处于选中状态
{
if (ro != sr)
{
for (int i = co; i + word.size() <= cop[ro].size(); i++)//第一排
{
string tem = cop[ro].substr(i, word.size());
if (tem == word) coun++;
}
for (int i = 0; i + word.size() <= sc; ++i)//最后一排
{
string tem = cop[sr].substr(i, word.size());
if (tem == word) coun++;
}
for (int i = ro + 1; i < sr; ++i)
{
for (int j = 0; j + word.size() <= cop[i].size(); ++j)
{
string tem = cop[i].substr(j, word.size());
if (tem == word) coun++;
}
}
}
else//在同一排
{
for (int i = co; i + word.size() <= sc; ++i)
{
string tem = cop[ro].substr(i, word.size());
if (tem == word) coun++;
}
}
}
else
{
for (int i = 0; i <= cop.size() - 1; ++i)
{
for (int j = 0; j + word.size() <= cop[i].size(); ++j)
{
string tem = cop[i].substr(j, word.size());
if (tem == word) coun++;
}
}
}
cout << coun << endl;
}
//字数统计
void count_char()
{
int res_c=0;
if (flag)//处于选中状态
{
if (sr != ro)
{
for (int i = ro + 1; i < sr; ++i)
{
for (int j = 0; j < cop[i].size(); ++j)
{
if (cop[i][j] != ' ') res_c++;
}
}
for (int i = co; i < cop[ro].size(); ++i)
{
if (cop[ro][i] != ' ') res_c++;
}
for (int i = 0; i < sc; ++i)
{
if(cop[sr][i]!=' ') res_c++;
}
}
else//同一行
{
for (int i = co; i < sc; ++i)
{
if (cop[ro][i] != ' ') res_c++;
}
}
}
else//不处于选中状态,统计记事本中所有字符
{
for (int i = 0; i < cop.size(); ++i)
{
for (int j = 0; j < cop[i].size(); ++j)
{
if (cop[i][j] != ' ') res_c++;
}
}
}
cout << res_c << endl;
}
//复制
void copy_now()
{
//ban.clear();
if (flag)//
{
ban.clear();
if (sr == ro)//在同一行
{
string tem = cop[ro].substr(co, sc - co);
ban.push_back(tem);
}
else
{
string a = cop[ro].substr(co);
ban.push_back(a);
for (int i = ro + 1; i < sr; ++i)
{
ban.push_back(cop[i]);
}
string b = cop[sr].substr(0, sc);
ban.push_back(b);
}
}
else if(cop[row].size()!=0)//复制当前行
{
ban.clear();
ban.push_back(cop[row]);
}
}
//打印
void print()
{
for (int i = 0; i != cop.size(); ++i)
{
cout << cop[i] << endl;
}
}
int main()
{
FILE* outfp;
//freopen_s(&outfp, "out.txt", "w", stdout);
cin >> n;
for (int i = 0; i < n+50; ++i)
{
string op;
cin >> op;
if (op == "MOVE") mov();
else if (op == "INSERT") inser();
else if (op == "REMOVE") remove();
else if (op == "SHIFT") shift();
else if (op == "FIND") find_it();
else if (op == "COUNT") count_char();
else if (op == "COPY") copy_now();
else if (op == "PRINT") print();
}
//fclose(stdout);//关闭文件
return 0;
}
虽然是去年写的,但这道题我记得出bug是没头没尾的
记忆比较深刻,当时卡在这里,很奇怪,幸好助教给了数据集文本(否则只能去试(世)一逝),把输出重定向到一个文本文件里面然后比对助教给的正确答案,发现错在了4770行之后,打断点调试后发现当i在四千七八百的时候,主循环莫名就终止了(蚌住了),当时看了一下qq群里的常见错误(结果一个都没对上),没办法我就把i扩大50,结果就过到100,但是还是没想通,怎么会改变i的值导致循环提前终止了。bug莫名其妙,只能说代码大概思路是对的,虽然我做的还是很艰难(哭)。