山东大学程序设计思维-记事本

题目描述

记事本是 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 列时,选中字段如下图所示:

当前 处于选中状态 时,对于不同的情况,需要按照序号依次执行以下操作:

  • 若进行光标移动

    1. 退出选中状态
    2. 尝试进行光标的移动(无论光标最终是否移动,都会退出选中状态)。
  • 若进行输入

    1. 将选中内容替换为输入内容;
    2. 退出选中状态
  • 若进行删除

    1. 删除当前选中内容;
    2. 退出选中状态
  • 若再次启动粘滞功能退出选中状态,但保留上一次选中字段的 记录点 作为当前记录点

  • 若进行查找,字数统计,复制,打印操作,则在操作后仍然保持选中状态

查找

输入 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莫名其妙,只能说代码大概思路是对的,虽然我做的还是很艰难(哭)。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值