1 题意如下:
一些信息编码方案要求一个编码信息被分为两部分来传输:第一部分叫做编码头,包括了信息的特征;第二部分是一个代表信息的编码,你必须写一个程序以如下的方案给信息解码。
程序所用编码方案的核心是如下的一个由‘0’和‘1’组成的“关键字”的序列:
0,00,01,10,000,001,010,011,100,101,110,0000,0001,…,1011,1110,00000,…
这个序列中的第一个关键字的长度是1,接下来3个的长度是2,接下来7个的长度是3,接下来15个的长度是4,等等。如果两个相邻的关键字长度相同,第二个关键字可以由第一个关键字加1得到(二进制),注意在序列中没有只包含‘1’的关键字。
这些关键字依次映射编码投中的字符。这就是说,第一个关键字(0)映射编码头中的第一个字符,第二个关键字映射编码头中的第二个字符,第K个关键字映射编码头中第K个字符。例如,如果编码头是:
AB#TANCnrtXc
那么0就映射A,00映射B,01映射#,10映射T,000映射A,…,110映射X,0000映射c。
编码信息只包括‘0’和‘1’以及可以被省略的可能的换行符。信息被划分成段,每段的头3个数字以二进制方式表示该段中的每个关键字的长度。例如,如果头3个数字是010,那么该段余下的数字就可由长度为2的关键字(00,01或10)组成。每段的结尾是一个与段中关键字长度相同的全部为‘1’的串,因此一个关键字长度为2的段由11终止。所有的编码信息由000终止(表示一个关键字长度为0的段)。通过将段中的关键字一个一个的转换成编码头中想映射的字符,信息得到了解码。
1.输入
输入文件包括一些测试数据。每个测试数据由一个独占一行的编码头和占据若干行的编码信息组成。事实上编码头的长度仅受关键字的最大长度7(二进制111)限制。如果编码头中有某个字符重复出现,几个关键字将映射这一个字符。编码信息只包括‘0’和‘1’,并且是根据上述编码方案合法编码的。也就是说,信息由一个3位长的数字序列开始,以一个适当长度的全‘1’的序列结束。同一个段中的关键字长度都是一样的。并且都与编码头中的字符相对应,信息由000终止。换行符可以出现在编码信息中的任何部分,它们不被认为是信息的一部分。
2.输出
对于每个测试数据,你的程序必须在不同的行上写出解码后的信息,在各组信息之间不应有空行。下面是样例输入及相应的正确输出。
3.样例输入
TNM AEIOU
0010101100011
1010001001110110011
11000
$#**\
0100000101101100011100101000
4。样例输出
TAN ME
##*\$
2 分析如下:
由题意可知编码方案是有一定规律的,如编码头的第一个字符映射第一个关键字,其长度为1,编码头的第2个字符至第4个字符分别映射第2个关键字至第四个关键字,长度为2,以此类推,我们就可以得出如下图所示的,不同位置的编码头其映射对应的位置。
图1 编码头映射图
从图中可以看出,每个相同长度的编码头都有一个起始位置,由于编码头的长度不大于二进制(111),故最长有7位,即编码头的长度范围从二进制001~111,共7个长度,故每个长度对应的起始位置共有如下数组表示——key_start[7] = {0,1,4,11,26,57,140}表示。然后从图1可以看出,相同长度下的编码头都可以从一个起始位置加上相应的偏移量offset获得。如我们要找‘E’的对应的偏移量,由映射关系我们可知,‘E’对应“001”(看题意关键字映射),而’E’在数组的下标index=5,而其对应的编码长度为3,即其开始位置key_start = 4,然后其所在的偏移量为001(二进制),对应十进制为1,即实际地址为real_pos = key_start + offset = 4+1 = 5。刚好符合E所在的数组下标位置。因此,通过这种开始位置加上偏移量来对应编码头数组的下标来得到我们想要的信息。而这也是从编码信息解码出我们想要的信息关键所在。
3 算法如下:
1 从文件中获取测试数据,因为数据可能包括编码头和编码信息的多重组合,如题目输入样式那种,故分批处理,一个编码信息对应一行编码头的组合来处理。读取测试数据中,把编码头读进p_inf[]数组中,并以换行符结束该数组的读取。
2.开始解码。根据读取3为关键字,判断是否遇到“000”,是则退出放回主函数,否则进入步骤3
3 得出编码信息中3位关键字,把相应的char先换成int,再根据位数转换成十进制数,得出关键字长度bit_num
4 根据bit_num,设定临时变量为bit_num长的全1的字符数组
5 根据4得到的全1数组,判断测试数据是否遇到bit_num长的全1的字符子串,是则回到2步骤继续,否则,进入步骤6
6 根据bit_num长度,先把char型转换成int型,再转换成10进制,得出offset,然后根据bit_num,得出开始位置start_pos = key_start[bit_num -1];最后得出相应bit_num长位的编码信息所对应的编码头信息的下标为real_pos = start_pos + offset.最终根据下标得出想要解码的信息。
4 编码实现如下:
/***************************************************
** **
**@autor: ZZP HITSGS **
**@function:信息编码解码 **
** **
***************************************************/
#include <stdio.h>
#include <String.h>
#include <malloc.h>
#include <stdlib.h>
#define file1 "1.txt" //该文件用于保存编码头和编码信息,为测试文件
#define file2 "2.txt" //该文件用于保存解码之后的信息
#define BUF_NUM 64
/***************************************************
** **
**@function:测试文件输入,返回文件句柄 **
** **
***************************************************/
FILE* inputInfo(){
char filebuf[BUF_NUM];
FILE *fp1 = NULL;
fp1 = fopen(file1,"w+");
if(fp1 == NULL){
printf("can't open file:\n");
exit(-1);
}
while(1){
gets(filebuf); //输入一行字符串
if(strcmp(filebuf,"exit")==0){
break;
}
fputs(filebuf,fp1); //写到文件1缓冲区
fflush(fp1); //刷新缓冲区
if(strcmp(filebuf,"\n") == 0){
continue;
}
fputs("\n",fp1); //新行
fflush(fp1); //刷新
}
return fp1; //返回FILE指针
}
/***************************************************
** **
**@function:根据bit位,把二进制换成十进制 **
** **
***************************************************/
int stringToInt(FILE* fp,int bit){
int i=0;
int bit_num = 0;
while(bit){
char ch = fgetc(fp);
if(ch != '\n'){ //去除空格情况
bit--;
bit_num += (ch-48)<<bit; //这里主要把bit位二进制数转换成10进制数
}
}
return bit_num; //返回int型
}
/***************************************************
** **
**@function:根据每个编码信息解码, **
**所获取信息保存进文件2 **
** **
***************************************************/
void writeToFile(FILE * fp,char *p_real_inf){
fputs(p_real_inf,fp);
fflush(fp);
fputs("\n",fp);
fflush(fp);
}
/***************************************************
** **
**@function:从编码信息的首位置开始分析 **
** **
***************************************************/
void doAnalysis(FILE *fp,char *p_inf,char *out_inf){
int bit_num = 0;
int real_pos = 0;
int offset = 0;
int key_start[7] = {0,1,4,11,26,57,140}; //每个关键字的开始位置
int all_ones[7] = {1,3,7,15,31,63,127}; //每个关键字的全1十进制
bit_num = stringToInt(fp,3); //得到每个段的位数
while(bit_num){ //是否到达末尾000处
while(1){
offset = stringToInt(fp,bit_num); //得到相同关键字的偏移量
if(offset == all_ones[bit_num - 1]){ //若是全1情况,则退出该bit_num关键字的循环
break;
}else{
real_pos = offset + key_start[bit_num-1]; //真正地位置
*(out_inf++) = p_inf[real_pos]; //获取解码后的信息
}
}
bit_num = stringToInt(fp,3);
}
*out_inf = '\0'; //结束符
}
/***************************************************
** **
**@function:先把测试数据写入文件1,从文件1读取 **
**第一行编码头,然后从编码头开始,边读取边解码 **
** **
***************************************************/
int main(void){
FILE *fp1 = NULL,*fp2 = NULL;
char p_inf[BUF_NUM];
char p_real_inf[BUF_NUM];
char ch;
int key_flag = 1;
int m=0;
fp2 = fopen(file2,"w+");
if(fp2 == NULL){
printf("can't open file:\n");
exit(-1);
}
printf("please input the test information,quit by \"exit\":\n");
fp1 = inputInfo(); //输入测试数据
fseek(fp1,0,SEEK_SET); //定位到文件开始处
memset(p_inf, 0, sizeof(p_inf)); //置0
memset(p_real_inf, 0, sizeof(p_real_inf));
ch = fgetc(fp1);
while(ch != EOF){
if(key_flag && ch == '\n'){ //直到遇到空格,表示原始信息已经读完,只有一行
key_flag = 0; //置标志位为0,表示已经读完
p_inf[m] = '\0';
}
if(key_flag){ //若标志位还未读完,则赋值给p_inf所指向的内存
p_inf[m++] = ch;
ch = fgetc(fp1);
}else{ //否则,开始分析
doAnalysis(fp1,p_inf,p_real_inf); //从原始信息首位置开始读取分析
writeToFile(fp2,p_real_inf);
memset(p_inf, 0, sizeof(p_inf));
memset(p_real_inf, 0, sizeof(p_real_inf));
m = 0;
key_flag = 1; //一个编码头和编码信息的组合结束,从新的一组组合开始,标志位重新置1
ch = fgetc(fp1); //换行符
if(ch == EOF){
break;
}
ch = fgetc(fp1); //到编码头的首位置
}
}
fseek(fp2,0,SEEK_SET); //定位到文件开始处
printf("after decode:\n");
ch = fgetc(fp2);
while(ch != EOF){
printf("%c",ch);
ch = fgetc(fp2);
}
printf("\n");
fclose(fp1);
fclose(fp2);
return 0;
}