传送门在这里:[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
应输出为de
,3-4
应输出为34
。如果减号右边的字符按照ASCII
码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:d-d
应输出为d-d
,3-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分钟左右才做出来。