题目描述
李明正在学习使用文本编辑器软件 。与 、 等常用的键鼠配合使用的文本编辑器不同, 采用了键盘操控+模式切换的设计。初始时用户处于普通模式,此时用户可以通过键盘输入命令执行光标移动等操作,或者输入特定命令进入到编辑模式;而在编辑模式中,用户可以从当前光标位置开始,向文本输入内容,然后按 键切换回普通模式。
李明学习到了普通模式的如下指令。其中 为当前光标的位置,即第 行第 列, 表示 行当前的长度, 为当前文本总行数,数学符号 表示 与 的最小值,例如 :
从编辑模式按 键切换回普通模式时,光标位置位于编辑模式插入的最后一个字符处(假定每次进入编辑模式后至少向文本中插入一个字符)。初始时, 处于普通模式,光标位置为 。
假设当前文本如下,光标位置用粗斜体+下划线+红色标注。李明的键盘输入序列 jlliHello^kIWorld^,其中 ^ 用于表示 键:
对于李明的键盘操作, 表示下移一格再右移两格,光标按 的顺序移动(其中第二次右移因为触及本行末尾无事发生)。此时光标位置为 ,即第 行第 列。 编辑器界面如下:
之后 iHello^ 表示从当前光标位置进入编辑模式,将插入的内容 置于此时光标位置 的左侧,然后按 键返回普通模式。此时光标位置为 ,指向此次插入内容 的最后一个字符 , 编辑器界面如下。另外需注意,指令按键(例如本段示例中的小写字母 )仅在普通模式下才表示编辑器控制指令,在编辑模式中则作为文本内容输入:
然后按 键将光标移至上一行。由于上一行的长度小于当前光标所在的列数,因此光标被移到了上一行的末尾。此时光标位置为 :
最后一段 IWorld^ 表示,按 键将光标移至本行最左侧的字母 处,进入编辑模式并将文本 插入到字母 的左端,然后按 退出编辑模式,光标停在插入的最后一个字母 处,坐标为 。
已知文本编辑器的初始内容,以及李明的键盘操作序列,你需要输出操作结束后, 编辑器中的文本内容,以及最终光标所在的位置。
输入格式
输入文件的第一行是一个正整数 ,表示初始时编辑器中文本的行数;之后 行,每行一个仅包含大小写字母的字符串,表示该行初始时的内容;之后一行为一个仅包含大小写字母和 ^ 的字符串,表示李明的键盘操作序列。输入保证操作序列结束后 处于普通模式,每次进入编辑模式时至少插入一个字符。
输出格式
输出文件共 行。前 行依次为操作结束后文本编辑器中每一行的内容,最后一行为两个正整数 ,表示操作结束后光标的位置。
样例
样例输入
3
ABC
HI
OPQRS
jlliHello^kAWorld^jAXyz^jhhaUvw^
样例输出
WorldABC
HHelloIXyz
OPQUvwRS
3 6
数据范围与提示
初始时第 行的长度为 ,操作序列的总长度为 。所有数据均满足: 测试数据的分布如下:
提示与说明
注意区分数字 、小写字母 与大写字母 。本题中光标右移指令为小写字母 ,行首插入的指令为大写字母 。 本题目的细节(例如光标的移动策略)与现实中的 编辑器存在差异,作答时以本题的描述为准。
思路与部分实现
首先注意到题目经常要求我们插入字符串,那么对于这种用心险恶的出题人,我们可以用 这神奇的数据类型帅他一脸(此题用 类型储存代码量要大很多 )
其次,题目要求输出光标最后所在位置的坐标,这里考虑用 两个变量分别储存横纵坐标。确定了基本思路,接下来就是对题目要求的各种操作一一实现:
如果当前字符 是 '',直接判断 是否大于 :如果是,将横坐标 减一。
if(str[i]=='h')
if(y>0) y--;
如果当前字符 是 '',直接判断 是否小于 :如果是,将纵坐标 加一,但由于 行的长度与 行的长度不一定相等, 可能指向无,所以在 与 行的长度之间取较小值。
if(str[i]=='j')
if(x<n-1){
int len=ans[++x].size()-1;
//读入时下标从0开始,所以这里要减一
y=min(y,len);
}
如果当前字符 是 '',处理步骤与上文基本一致,这里不做过多叙述。
if(str[i]=='k')
if(x>0){
int len=ans[--x].size()-1;
y=min(y,len);
}
如果当前字符 是 '',直接判断 是否处于这一行的末尾:如果不是,将 加一。
if(str[i]=='l')
if(y<ans[x].size()-1) y++;
如果当前字符 是 '' 或 '',就迎来了本题最大的难点,这里提供一个较为巧妙的思路:我们可以定义一个 类型的字符串 以储存光标当前位置右侧的所有字符,以及 储存将要插入的字符,然后用 库中自带的 函数删除光标当前位置右侧的所有字符,接着更新横坐标于插入单词的末尾(即 的长度加此时 行的长度),最后在 行的末尾加入 以及 。至于 '' 的情况怎么处理,只需在储存 时将当前光标指向的字符也加入进去,并在之后一同删除,这就是处理 '' 和 '' 时的两个不同点。下面举个例子方便理解:
EHIAT
if(str[i]=='a')
假设光标此时指向I且要插入的字符串为"XyZ",那么kk="AT",这行字符串删除光标右侧所有字符后为"EHI",
接着更新横坐标y指向5的位置(下标从0开始计算),最后在末尾加入kk和要插入的字符串,即:
EHI + XyZ + AT = EHIXyZAT
EHIAT
if(str[i]=='i')
假设光标此时指向I且要插入的字符串为"XyZ",那么kk="IAT",这行字符串删除光标指向的字符及光标右侧所有字符后为"EH",
接着更新横坐标y指向4的位置(下标从0开始计算),最后在末尾加入kk和要插入的字符串,即:
EH + XyZ + IAT = EHXyZIAT
最后提醒一点:千万注意操作的先后顺序!
if(str[i]=='a'||str[i]=='i'){
string k,kk;
int L=str[i]=='a'?y+1:y;
while(str[++i]!='^') kk+=str[i];
for(int l=L;l<ans[x].size();l++) k+=ans[x][l];
ans[x].erase(L);
y=ans[x].size()+kk.size()-1;
ans[x]+=kk+k;
//一定要注意操作的先后顺序!
}
如果当前字符 是 '', 直接在改行末尾加入 (要插入的字符串),然后更新横坐标为当前行的末尾。
if(str[i]=='A'){
string k;
while(str[++i]!='^') k+=str[i];
ans[x]+=k;
y=ans[x].size()-1;
}
如果当前字符 是 '',先更新横坐标为 的末尾,然后 。
if(str[i]=='I'){
string k;
while(str[++i]!='^') k+=str[i];
y=k.size()-1;
ans[x]=k+ans[x];
}
到这里代码的主体部分就结束了,接下来请欣赏我那码风新奇的代码。
完整代码
#include<iostream>
using namespace std;
string ans[105],str;
int main()
{
int n,x=0,y=0;scanf("%d",&n);
for(int i=0;i<n;i++) cin>>ans[i];
cin>>str;
for(int i=0;i<str.size();i++)
if(str[i]=='h')
if(y>0) y--;
else if(str[i]=='j')
if(x<n-1){
int len=ans[++x].size()-1;
y=min(y,len);
}
else if(str[i]=='k')
if(x>0){
int len=ans[--x].size()-1;
y=min(y,len);
}
else if(str[i]=='l')
if(y<ans[x].size()-1) y++;
else if(str[i]=='a'||str[i]=='i'){
string k,kk;int L=str[i]=='a'?y+1:y;
while(str[++i]!='^') kk+=str[i];
for(int l=L;l<ans[x].size();l++) k+=ans[x][l];
ans[x].erase(L);y=ans[x].size()+kk.size()-1;ans[x]+=kk+k;
}else if(str[i]=='A'){
string k;while(str[++i]!='^') k+=str[i];
ans[x]+=k;y=ans[x].size()-1;
}else if(str[i]=='I'){
string k;while(str[++i]!='^') k+=str[i];
y=k.size()-1;ans[x]=k+ans[x];
}
for(int i=0;i<n;i++)
cout<<ans[i]<<endl;
printf("%d %d",x+1,y+1);
return 0;
}
总结
题目看起来虽然复杂,实际上就是道简单的模拟,多注意细节即可。