文章目录
题目描述
夏娜酱最喜欢菠萝包了,于是威尔艾米娜从天道宫下来买了一大堆菠萝包。夏娜酱先将这些菠萝包按某种顺序排成一列,然后给每个菠萝包起了个名字,名字都是单个 的大写字母A-Z。小白想检验一下夏娜酱的学习成果,要求夏娜酱以下两种方式将这些菠萝包重排为新序列,使这些菠萝包的名字组成的新序列字典序最小。
1.从原序列的前端取出一个菠萝包,放在新序列的尾端
2.从原序列的尾端取出一个菠萝包,放在新序列的尾端
注:字典序的比较如:ABCDE<BCDEA, ABCDE<ABDCE。
聪明的夏娜酱已经完成这个任务了,现在,轮到你来造出这样的新序列了。
输入:
输入包含多组用例,每组用例占两行,在每组用例中,第一行为一个整数n(1<=n<=2000),第二行为一个只包含大写字母的原序列。
输出:
对每组用例,输出符合要求的新序列,每80个字符换一次行,当然串尾要换行。
分析:
光用脑子想感觉很简单,就是每次找两端的较小值放入新数组里。如果两端相等,就不断往里找,离较小值较近的一端放入新数组。
然而,实际上,具体编程的时候遇到了严重的问题, 就是如何考虑周全两个指针将要碰到一起甚至交错的时候的情况。
代码
#include <stdio.h> #include <string.h> #define CONTAIN 2005 void print(char *str); int main() { freopen("/home/langley/programe/sample.txt","r",stdin); int n = 0;//number of letters while(scanf("%d",&n) != EOF) { char str[CONTAIN] = {0};//string of input getchar(); for(int i = 0;i < n;i ++) { str[i] = getchar(); } char display[CONTAIN] = {0};//string of output int p = 0;//a whole varible as the index of display int start = 0,end = strlen(str) - 1;//the range of the string while(1) { if(str[start] != str[end]) { if(str[start] < str[end]) { display[p] = str[start]; start ++; } else { display[p] = str[end]; end --; } } else { int temp_s = start,temp_e = end; for( ;temp_s <= temp_e && str[temp_s] == str[temp_e] ;temp_s ++,temp_e --); if(temp_s < temp_e) { if(str[temp_s] < str[temp_e]) { display[p] = str[start]; start ++; } else if(str[temp_s] > str[temp_e]) { display[p] = str[end]; end --; } } else if(temp_s >= temp_e)//sys { display[p] = str[start]; start ++; } } //No need to consider this much /*if(start == end) { display[p+1] = str[start]; break; }*/ if(start > end)//you could have put this in while(); break; p ++; } display[n] = 0; print(display); } return 0; } void print(char *str) { char *p = str; while(1) { for(int i = 0;i < 80 && *p != 0;i ++) { putchar(*p); p ++; } if(*p == 0) break; putchar(10); } putchar(10); }
这里主要困住我的是两个问题:(1)从原数组取数字时的循环结束条件,(2)按80一行打印
(1)对于最外层的循环,是start > end的时候跳出循环还是start >= end 的时候就要跳出来?
当两个字母相同的时候开始的内层的循环,是temp_s > temp_e的时候跳出来还是temp_s >= temp_e 的时候跳出来?
这些问题在设计算法的时候就应该充分的考虑清楚,否则输入代码的时候会越来越凌乱。
首先看较小的循环,进入这个循环有个前提条件,就是数组左右端字母是相等的。要考虑的就是偶数和奇数的对称数组的区别。比如,BAB和BAAB。对于BAB,temp向中间靠拢会指向同一个值,因为有对称的结构,此时跳出还是再走一步跳出都没有关系。而对于BAAB,temp逐渐向里缩会出现交错,即temp_s
> temp_e,跳出循环。所以temp_s >= temp_e或temp_s > temp_e都可以为跳出循环的条件,跳出来之后呢?其实这样对称的情形不管怎么打印都无所谓了,所以都打印start就好了。
> temp_e,跳出循环。所以temp_s >= temp_e或temp_s > temp_e都可以为跳出循环的条件,跳出来之后呢?其实这样对称的情形不管怎么打印都无所谓了,所以都打印start就好了。
看较大的循环,当start与end碰到一起的时候,如果跳出,这个位置的字母就会miss;如果不跳出,会进入str[start] == stt[end]的情况,不会进入小循环,直接打印str[start],然后start > end,符合我们的预想,这个时候应该跳出。所以循环继续的条件应该是start
<= end。
<= end。
除此之外,还要考虑一下start和end会跳出数组范围的情况,即str[start]和str[end]某个位置不在输入的值的范围之内,此时网教的不知装了什么奇葩插件的gcc会Runtime Erro ,提示无效内存引用。这个问题我在之前一片日志《集合求交》中着重提醒过这样的问题。不过幸运的是,这道题如果按照上面的条件终止循环,不会出现这样的问题。因为start的值一直小于end,充其量只会比end大1,而当start
== end + 1时,已经达到的循环终止条件,start和end不会作为索引取数组中的值,直接跳出。只要start别跳出数组的范围(这里是2005),即使该索引位置没有值也无关紧要。
== end + 1时,已经达到的循环终止条件,start和end不会作为索引取数组中的值,直接跳出。只要start别跳出数组的范围(这里是2005),即使该索引位置没有值也无关紧要。
(2)主要困住我的是第二个问题,按80个换行打印。考虑两种情形,字母是或不是80的整数倍。
如果仅仅是输入80个字母换行,然后全输入完时换行,就会出现80个字母的序列换两行的现象。
我一开始是这么写的print函数:
void print(char *str) { char *p = str; while(1) { for(int i = 0;i < 80;i ++) { if(*p != 0) { putchar(*p); p ++; } else if(*p == 0 ) { if(i != 0) { putchar(10); return ; } else return ; } } }
这样的打印方法一直过不了网教,在我的电脑上却“似乎”是对的。
起初一直不明白为什么,知道我用全屏在终端上打印出来
真相才浮出水面,原来codeblocks的袖珍控制台一行只能显示80个字符,包括终端在没有最大化的时候也是一行显示80个字符,超过80会自动折行……原来前面那段代码错的如此离谱却因为这个一直没有发现。
当初对80的整数倍个数考虑复杂了,其实只要先检测是否到达结尾再换行就好了。如果已经到达字符串末尾,直接跳出循环,打印程序最终的回车就好了。
修改之后就可以正确显示了。