一、本题主要算法
本题算法设计方面的一个核心技巧就是我们可以不必以字符串的形式来保存编码,而是可以直接将编码理解为二进制,然后用一个二元组(对应十进制的值和二进制数长度)来进行表示。本题的难度还体现在对输入输出的控制方面,因为题目里面讲到:
Carriage returns may appear anywhere within the message part. They are not to be considered as part of the message.
回车可以出现在编码文本部分的任何地方。它们不应被视为文本的一部分。
如样例输入就是这个样子:
0010101100011
1010001001110110011
11000
二、代码及题解
代码是模仿紫书上的代码,本文主要是对代码进行了更加详细的注释,以便于理解
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//自顶向下的编程思想,我们在本题编写几个子函数来解决问题
int readchar(){ //找到不是换行符的字符
while(true){
int ch=getchar();
if(ch!='\n' && ch!='\r') return ch; //这里的'\r'也是一种换行符,和'\n'的区别在于其执行结束后,当前位置移动到本行开头
}
}
int readint(int c){ //读取特定位数的二进制数,并将其转化为十进制数
int v=0;
while(c--) v=v*2+readchar()-'0';
return v;
}
int code[8][1<<8];
int readcodes(){
memset(code,0,sizeof(code));
code[1][0]=readchar(); //直接调到下一行进行读取,如果输入已经结束,则会读取到EOF
for(int len=2;len<=7;len++) //编码头最大到7,也就是二进制的111(题目描述中有),这里是在试编码头的长度
for(int i=0;i<(1<<len)-1;i++){ //一次次确定01串的长度
int ch=getchar(); //先读一下试试
if(ch==EOF) return 0; //读到文件尾,则要结束程序(不会有新的编码头了)
if(ch=='\n' || ch=='\r') return 1; //编码头读取完毕
code[len][i]=ch; //这句和上一句的顺序不能搞反了
}
return 1;
}
void printcodes(){
for(int len=1;len<=7;len++)
for(int i=0;i<(1<<len)-1;i++){
if(code[len][i]==0) return;
printf("code[%d][%d] = %c\n",len,i,code[len][i]);
}
}
int main(){
//freopen("in.txt","r",stdin); //本题在调试的时候,建议采用文件输入输出
// freopen("out.txt","w",stdout);
while(readcodes()){ //找编码头,没有编码头的时候,程序即停止
//printcodes() //test1
while(true){ //先用死循环,后面可以保证跳出
int len=readint(3); //读取编码文本的前三个数,代表小节中每个编码长度
if(len==0) break; //如果出现000则编码结束
//printf("len=%d\n",len); //test2
while(true){
int v=readint(len); //按照编码长度读取各个小节
//printf("v=%d\n",v); //test3
if(v==(1<<len)-1) break; //出现全1则小节结束
putchar(code[len][v]); //输出解码
}
}
putchar('\n'); //一定不要忘记换行
}
return 0;
}
紫书上本题代码的突出优点还在于采用了自顶向下的结构,如果单看主函数的话,对问题处理的时候,层次十分地分明,在整个结构层次清楚之后,再将子函数进行完善,以使其各自发挥功能即可。此外,代码里还提供了一个测试用的函数printcodes,还有三个测试点。这样的测试方式不止适用于本题,它更是一种测试思想,也适用于其它较复杂题目。
三、作者
Bowen
作者水平有限,如有纰漏,敬请斧正。
欢迎大家一起学习交流。