题目来源:2021牛客OI赛前集训营-普及组(第四场)
手写体的 “uu” 和 “w”,“nn” 和 “m” 简直让人难以辨认。 某人拿到了一个手写体字符串s ,她只能按照这个字符串某种可能的形式将其记 录下来。具体来说,对于字符串内的一个子串 “uu”,她有可能记录成 “w”,对于子串 “w”,可能记录成 “uu”。同样对于子串 “nn”,可能记录成 “m”,对于子串 “m”,可能记录成 “nn”。而对于 u,w,n,m 以外的其它字符,则不会出现记录错误。 更糟糕的是,她记录完后,下一个人按照她的记录进行再一次记录时又会产生同样的错误。
现在拿到牛妹拿到了不知道被反复记录了多少次的字符串 t,万幸的是她知道了 这个字符串原本的长度为n,即∣ s ∣= n。她现在希望你帮她恢复出这个字符串s, 如果有多种可能性,任意一种符合题意的s都会被认为是正确的。
第一行一个整数T,表示数据组数。 接下来每两行代表一组数据。对于每一组数据, 其第一行,一个正整数n ,表示原串s 的长度; 其第二行,一个字符串t ,表示s经过反复记录后的字符串。保证t串仅由小写字母组成。
输出T行,第i行对应第i组数据的原串s。如果有多种可能性,任意一种符合题意的s都会被认为是正确的 。
数据保证存在至少一个答案。
35
abcw
7
xuwuxnmnx
3
wm
abcuu
xwwxmmx
uum
输入样例1说明
第一组数据,“abcuu” 被记录成 “abcw”。
第二组数据, “xwwxmmx” 被记录成 “xuuuuxnnnnx” ,再次被记录成 “xuwuxnmnx”。
第三组数据,“uum” 被记录成 “wm”,还有另一个可能的答案是 “wnn”。
对于 20%数据,满足1 ≤ n, ∣ t ∣≤ 10。
对于 50%数据,满足1 ≤ n, ∣ t ∣≤ 1000。
对于另外 10%数据,满足字符串t中不含有u, w, n, m。
对于 100%数据,满足1 ≤ n, ∣ t ∣≤ 100000, 1 ≤ T ≤ 10。
所有数据保证t中仅含有小写字母,保证至少存在一个答案。
根据题目,m
可以换为nn
,nn
可以换为m
,w
可以换为uu
,uu
可以换为w
。
因此,我们可以先求出所给字符串的长度,算出与目标长度之间的差值。如果差值为0,说明当前长度等于目标长度,直接输出所给字符串即可。
如果所给字符串长度大了,就从前往后枚举,将所有nn
或uu
换成m
或w
,直到当前长度等于目标长度。
如果所给字符串长度小了,就从前往后枚举,将所有m
或w
换成nn
或uu
,直到当前长度等于目标长度。
但是,仔细观察样例中的第二组数据就会发现,根据上面的算法,对第二个字符串做不了任何改动。这是由于形如uwu
的子串其实可以变成uuuu
再变为ww
,使长度减一,而上述算法只有在两个u相邻的时候才能修改。但特殊判断这类子串未免太过繁杂,所以就产生了第二种思路。
参考uwu
变成uuuu
再变成ww
的过程,我们可以先将原字符串的m
和w
全部处理成nn
和uu
,存在一个新的数组s1中,这时s1的长度一定是可得字符串中最大的,因此用s1的长度减去目标长度就是需要减少的长度,因为修改一次可以使长度减一,所以这个长度就是修改的次数。这时只需要从前往后枚举,遇到相邻的uu
或nn
就输出w
或m
即可。
首先建立变量cntn和cntu,统计n和u连续的数量
情况如下:
可能有:n*
,u*
,**
对于前两种情况,需要将n或u连同当前字符一起输出,也就是说,如果cntn=1或者cntu=1时是不用输出的,然后重置cntn和cntu
可能有:un
,nn
,*n
如果cntu=1,先输出u,因为u只有一个,不可能换成w,并将cntu重置
如果cntn=2,直接输出m,并将cntn重置
对于第三种情况,情况一会先输出*,而当前的n会留到下一步操作来判断
可能有:nu
,uu
,*u
如果cntn=1,先输出n,因为n只有一个,不可能换成m,并将cntn重置
如果cntu=2,直接输出w,并将cntu重置
对于第三种情况,情况一会先输出*,而当前的u会留到下一步操作来判断
1.转换为新字符串时,遇到"w"和"m",len1要加二,字符串要存两个字母(uu或nn)
2.在特判输出时,不能直接输出s1,否则会输多(我也不知道为什么),要把s1一个一个字符输出
#include
using namespace std;
int t,n,val;//t多组数据,n目标长度,val表示与目标长度的差值
char s[100005],s1[2000005];//s存储所给字符串,s1存储转换后的字符串
int main()
{
scanf("%d",&t);
for(int tt=1;tt<=t;tt++)
{
scanf("%d%s",&n,s);
int len=strlen(s);//所给字符串的长度
int len1=0;//新字符串的长度
for(int i=0;i<len;i++)//将所给字符串换成没有"w"和"m"的字符串
{
if(s[i]=='w') s1[len1++]='u',s1[len1++]='u';//s1要加入两个“u”,len1也要加二
else if(s[i]=='m') s1[len1++]='n',s1[len1++]='n';//同上
else s1[len1++]=s[i];//将不是“w”和“m”的字符存入新字符串
}
val=len1-n;//计算当前长度与目标长度的差值,也就是需要转换的次数
if(val==0)//特判,val=0代表len1=n,说明当前长度就是要求长度,直接输出(见上细节2)
{
for(int i=0;i<len1;i++) printf("%c",s1[i]);
printf("\n");
continue;
}
int cntu=0,cntn=0,i=0;//cntu统计连续的u,cntn统计连续的n,i枚举s1字符串
while(val)//对s1进行遍历修改,直到用完次数
{
if(s1[i]=='n')
{
if(cntu==1) printf("u"),cntu=0;//上一个字符是u,但只有一个u就到n了
cntn++;
if(cntn==2)//连续的两个n,转换为m
{
printf("m");
val--,cntn=0;//次数减一,cntn重置
}
}
else if(s1[i]=='u')
{
if(cntn==1) printf("n"),cntn=0;//上一个字符是n,但只有一个n就到u了
cntu++;
if(cntu==2)//连续的两个u,转换为w
{
printf("w");
val--,cntu=0;//次数减一,cntu重置
}
}
else if(cntu==1 || cntn==1) printf("%c%c",s1[i-1],s1[i]),cntu=cntn=0;//这个字符不是u也不是n,但前面有u或n
else printf("%c",s1[i]);//这个字符既不是u也不是n,前面也没有u和n,直接输出
i++;//枚举变量自增
}
while(i<len1) printf("%c",s1[i++]);//修改完毕后可能剩下一些字符,直接输出即可
printf("\n");
}
return 0;
}