洛谷P1098题解

传送门在这里:[NOIP2007 提高组] 字符串的展开 - 洛谷

题意描述:

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于 d-h 或者 4-8 的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为 defgh 和 45678。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号 - ,减号两侧同为小写字母或同为数字,且按照 ASCII 码的顺序,减号右边的字符严格大于左边的字符。

(2) 参数 𝑝1:展开方式。𝑝1=1 时,对于字母子串,填充小写字母;𝑝1=2 时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。𝑝1=3 时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号 * 来填充。

(3) 参数 𝑝2:填充字符的重复个数。𝑝2=𝑘p2​=k表示同一个字符要连续填充 𝑘个。例如,当 𝑝2=3时,子串d-h 应扩展为 deeefffgggh。减号两边的字符不变。

(4) 参数 𝑝3​:是否改为逆序:𝑝3=1表示维持原来顺序,𝑝3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当 𝑝1=1、𝑝2=2、𝑝3=2 时,子串 d-h 应扩展为 dggffeeh

(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:d-e 应输出为 de3-4 应输出为 34。如果减号右边的字符按照 ASCII 码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:d-d 应输出为 d-d3-1 应输出为 3-1

由题意描述可以看出来,这是一道纯模拟题,但是坑点需要自己找出来,第一次只有40pts,就是因为下面两个坑点没找到,还消耗了我三次下载机会。。。

分析坑点:

①字符串首尾均可能出现'-'号,但是这些'-'号均应被省略而直接输出。譬如第8个测试点


Input :

3 1 1
-z-l-k-d-h-f-q-w-y-e-r-o-i-q-u-y-e-s-a-k-j-d-h-f-l-a-k-s-d-h-f-i-q-u-i-y-r-q-l-w-e-h-k-z-x-h-d-f-l-k

Correct output:

-z-l-k-d***h-f**********q*****w*y-e************r-o-i*******q***u***y-e*************s-a*********k-j-d***h-f*****l-a*********k*******s-d***h-f**i*******q***u-i***************y-r-q-l**********w-e**h**k**************z-x-h-d*f*****l-k


如果你跟我一样想从第0个字符遍历到第len-1个字符,你会惊讶的发现大大的WA。正确的做法应该是从第1个字符遍历到第len-2个字符。然后再做特殊处理。

②'-'号可能是连续的,这些连续的'-'号都应该被直接输出。譬如第6个测试点:


Input:

1 1 1
2-43-sdf-ewr0-j-9re-j0g-9e0-9as-d09jf-9asdjf0q-w-ejr0q-59jdsnf-9z-x9v0-sd9fn--q

Correct output:

2343-sdf-ewr0-j-9refghij0g-9e0123456789as-d09jf-9asdjf0qrstuvw-ejr0q-59jdsnf-9z-x9v0-sd9fn--q


所以这里在展开字符的时候应该再判断前面的是不是也是'-'号,不然你就会完美踩坑。

剩下的特殊情况题目中已经全部说明,这里不再赘述。

代码展示与分析:

#include <bits/stdc++.h>//万能头好像可以用了
using namespace std;
int p1,p2,p3,s_len;
string s,ans,result;//s是输入的字符串,ans是输出的字符串,result用于deal()函数中,这样定义是为了节省一点空间(但貌似根本就不需要)
string tu(string P){//手写的to_upper函数 
	for(int ii=0;ii<P.length();ii++){
		P[ii] += 'A'-'a';//将P[ii]变为大写,等价于p[ii] = to_upper(p[ii])
	}
	return P;
}
string deal(char l,char r){//用函数处理更加简洁易懂
	int l_a=(int)l,r_a=(int)r;//l和r的ASCII码
	for(int mid=l+1;mid<r;mid++){//mid∈(l,r)且mid∈N* 
		for(int k=1;k<=p2;k++){
            //题目中的第(3)点说明,用于增加p2个字符
			result += (char)mid;
		}
	}//先处理好(3)
	if(result[0]>='a'&&result[0]<='z'){//如果是字母压缩的情况 
		if(p1==2) result = tu(result);//转换为大写字母
		//否则不变(目前result就是纯小写字母) 
	} 
	if(p3==2) reverse(result.begin(),result.end());//题目中的第(4)点说明,这里也可以手写reserve函数,不过有现成的为啥要手写(滑稽)
	if(p1==3){//题目中的第(2)点说明,这里最后判断比较简单,如果开始就判断还要计算'*'号的个数,倒不如直接把字符替换成'*'即可
		for(int i=0;i<result.length();i++)
			result[i] = '*';
	}
	return result;
}
bool cmp(char a,char b){//题目中的第(1)点说明的判断
    //有很多测试点将数字和字母放在了一起,所以要判断是否为纯数字或纯字母
	if('0'<=a&&a<='9') return ('0'<=b&&b<='9');
	else return ('a'<=b&&b<='z');
}
int main(){
	cin >> p1 >> p2 >> p3 >> s;//正常的输入
	s_len = s.length();//正常的判断字符串长度
	ans += s[0];//坑点① 需要特殊处理第0个字符和第(s_len-1)个字符
	for(int i=1;i<s_len-1;i++){//还是坑点① 处理范围为{s[c]|c∈[1,s_len-1)};
		if(s[i]!='-') ans += s[i];//不是'-'号直接照抄
		else if(ans[ans.length()-1]=='-') ans+='-';//坑点② 这是用于判断是否为两个连续的'-'号的
		else if(s[i+1]-s[i-1]==1){//处理题目第(5)点说明,如果是'-'号就不鸟他,直接下一个循环
			continue;
		}
		else if(s[i-1]<s[i+1]&&cmp(s[i-1],s[i+1])){//用于处理题目第(1)点说明
			ans += deal(s[i-1],s[i+1]);//这里就开始进行字符串展开了,deal()函数就是用于展开字符串的
		}else{//如果是'-'号但是上面条件都把他抛弃了 那么就把他照抄下来
			ans += '-';
		} 
	}
	ans += s[s_len-1];//坑点① 特殊处理第(s_len-1)个字符
	cout << ans;
	return 0;
}

还是那句话,抄袭可耻,选择了信奥这条路,就需要自己动脑。这题我也花了15分钟左右才做出来。

  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值