【题解】double u

题目来源: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可以换为nnnn可以换为mw可以换为uuuu可以换为w
因此,我们可以先求出所给字符串的长度,算出与目标长度之间的差值。如果差值为0,说明当前长度等于目标长度,直接输出所给字符串即可。
如果所给字符串长度大了,就从前往后枚举,将所有nnuu换成mw,直到当前长度等于目标长度。
如果所给字符串长度小了,就从前往后枚举,将所有mw换成nnuu,直到当前长度等于目标长度。
但是,仔细观察样例中的第二组数据就会发现,根据上面的算法,对第二个字符串做不了任何改动。这是由于形如uwu的子串其实可以变成uuuu再变为ww,使长度减一,而上述算法只有在两个u相邻的时候才能修改。但特殊判断这类子串未免太过繁杂,所以就产生了第二种思路。

思路二

参考uwu变成uuuu再变成ww的过程,我们可以先将原字符串的mw全部处理成nnuu,存在一个新的数组s1中,这时s1的长度一定是可得字符串中最大的,因此用s1的长度减去目标长度就是需要减少的长度,因为修改一次可以使长度减一,所以这个长度就是修改的次数。这时只需要从前往后枚举,遇到相邻的uunn就输出wm即可。
首先建立变量cntn和cntu,统计n和u连续的数量
情况如下:

一、当前既不是u也不是n

可能有:n*,u*,**
对于前两种情况,需要将n或u连同当前字符一起输出,也就是说,如果cntn=1或者cntu=1时是不用输出的,然后重置cntn和cntu

二、当前为n

可能有:un,nn,*n
如果cntu=1,先输出u,因为u只有一个,不可能换成w,并将cntu重置
如果cntn=2,直接输出m,并将cntn重置
对于第三种情况,情况一会先输出*,而当前的n会留到下一步操作来判断

三、当前为u

可能有:nu,uu,*u
如果cntn=1,先输出n,因为n只有一个,不可能换成m,并将cntn重置
如果cntu=2,直接输出w,并将cntu重置
对于第三种情况,情况一会先输出*,而当前的u会留到下一步操作来判断

细节

1.转换为新字符串时,遇到"w"和"m",len1要加二,字符串要存两个字母(uu或nn)
2.在特判输出时,不能直接输出s1,否则会输多(我也不知道为什么),要把s1一个一个字符输出

code

#include<bits/stdc++.h>
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;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值