资源限制
内存限制:512.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
给定n个十六进制正整数,输出它们对应的八进制数。
输入格式
输入的第一行为一个正整数n (1<=n<=10)。
接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。
输出格式
输出n行,每行为输入对应的八进制正整数。
【注意】
输入的十六进制数不会有前导0,比如012A。
输出的八进制数也不能有前导0。
样例输入
2
39
123ABC
样例输出
71
4435274
【提示】
先将十六进制数转换成某进制数,再由某进制数转换成八进制。
解题思路
首先看到进制转换问题,我们首先考虑使用C语言自带的占位符转换。
%d
十进制占位符,long int、long long分别为%ld
,%lld
%o
八进制占位符,long int、long long分别为%lo
,%llo
%x
十进制占位符,long int、long long分别为%lx
,%llx
下面浅尝试一下:
#include<stdio.h>
int main()
{
long long int x;
scanf("%llx", &x); //十六进制输入
printf("%llo", x); //八进制输出
return 0;
}
当然结果可想而知,WA了。至于原因很简单,题目中说到每个十六进制数长度不超过100000。想一想长度是十万的十六进制数有多大吧,这绝对已经超过了long long int是能表示的最长长度了。那么开始认认真真做吧。
随后就想到把十六进制转成二进制并用字符串保存下来,然后由二进制再向八进制转化。我们知道,十六进制数转二进制占四个二进制位,八进制数转二进制占三个二进制位,所以转化出的二进制数位数是4的倍数,向八进制转化时需要位数是3的倍数,所以可能需要在二进制字串前面补0。同时我们需注意题目要求不允许有前导0,所以需要判断从第一个不为0的八进制数开始输出。
下面,上代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int main()
{
int n;
scanf("%d", &n);
getchar(); //吸收换行符,防止对后面的gets()产生影响
while(n--)
{
int sum;
char str[100001], str_o[400010];
gets(str);
int i=0, j=0, num = strlen(str);
if((4*num)%3 == 1) //判断转化后的长度是否能被3整除,不能则补0
str_o[0]='0', str_o[1]='0', j=2;
else if((4*num)%3 == 2)
str_o[0]='0', j=1;
for(i=0;i<num;i++)
{
switch(str[i]) //switch批量转化十六进制位,快速延长字符串长度
{
case '0':str_o[j]='0', str_o[j+1]='0', str_o[j+2]='0', str_o[j+3]='0';break;
case '1':str_o[j]='0', str_o[j+1]='0', str_o[j+2]='0', str_o[j+3]='1';break;
case '2':str_o[j]='0', str_o[j+1]='0', str_o[j+2]='1', str_o[j+3]='0';break;
case '3':str_o[j]='0', str_o[j+1]='0', str_o[j+2]='1', str_o[j+3]='1';break;
case '4':str_o[j]='0', str_o[j+1]='1', str_o[j+2]='0', str_o[j+3]='0';break;
case '5':str_o[j]='0', str_o[j+1]='1', str_o[j+2]='0', str_o[j+3]='1';break;
case '6':str_o[j]='0', str_o[j+1]='1', str_o[j+2]='1', str_o[j+3]='0';break;
case '7':str_o[j]='0', str_o[j+1]='1', str_o[j+2]='1', str_o[j+3]='1';break;
case '8':str_o[j]='1', str_o[j+1]='0', str_o[j+2]='0', str_o[j+3]='0';break;
case '9':str_o[j]='1', str_o[j+1]='0', str_o[j+2]='0', str_o[j+3]='1';break;
case 'A':str_o[j]='1', str_o[j+1]='0', str_o[j+2]='1', str_o[j+3]='0';break;
case 'B':str_o[j]='1', str_o[j+1]='0', str_o[j+2]='1', str_o[j+3]='1';break;
case 'C':str_o[j]='1', str_o[j+1]='1', str_o[j+2]='0', str_o[j+3]='0';break;
case 'D':str_o[j]='1', str_o[j+1]='1', str_o[j+2]='0', str_o[j+3]='1';break;
case 'E':str_o[j]='1', str_o[j+1]='1', str_o[j+2]='1', str_o[j+3]='0';break;
case 'F':str_o[j]='1', str_o[j+1]='1', str_o[j+2]='1', str_o[j+3]='1';break;
default:break;
}
j += 4; //用变量j实时记录当前字串长度,便于switch工作
}
int flag = 0; //这里插旗,判断第一个不是0的数出现
for(i=0;i<strlen(str_o)-2;i+=3)
{
int res = 4*(str_o[i]-'0') + 2*(str_o[i+1]-'0') + (str_o[i+2]-'0'); //二进制向八进制转化
if(res != 0) flag=1; //第一位不是0的数出现,旗子判定可以开始输出
if(flag != 0) printf("%d", res); //从第一位不是0的数开始输出
}
printf("\n");
}
return 0;
}
提交一下进行尝试,好的,运行超时。
但好在不是答案错误了,现在开始优化代码,首先检查程序最耗时的部分就是十六进制 -> 二进制 -> 八进制的转化过程了,考虑一下既然二进制字串都是0和1,那么我们就可以把储存方式从字符串换成数组,向八进制转化时就省下了ASCII码加减计算的一部分时间。而数组是不能用strlen()
函数的,所以就需要使用一个变量完整的记住二进制字串长度。巧的是,刚好十六进制转化二进制时储存长度的变量 j 可以供使用。这样又省下了转化八进制时循环调用strlen()
函数的时间。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int main()
{
int n;
scanf("%d", &n);
getchar();
while(n--)
{
int sum, str_o[400010];
char str[100001];
gets(str);
int i=0, j=0, num = strlen(str);
if((4*num)%3 == 1) //判断转化后的长度是否能被3整除,不能则补0
str_o[0]=0, str_o[1]=0, j=2;
else if((4*num)%3 == 2)
str_o[0]=0, j=1;
for(i=0;i<num;i++)
{
switch(str[i])//switch批量转化十六进制位,快速延长字符串长度
{
case '0':str_o[j]=0, str_o[j+1]=0, str_o[j+2]=0, str_o[j+3]=0;break;
case '1':str_o[j]=0, str_o[j+1]=0, str_o[j+2]=0, str_o[j+3]=1;break;
case '2':str_o[j]=0, str_o[j+1]=0, str_o[j+2]=1, str_o[j+3]=0;break;
case '3':str_o[j]=0, str_o[j+1]=0, str_o[j+2]=1, str_o[j+3]=1;break;
case '4':str_o[j]=0, str_o[j+1]=1, str_o[j+2]=0, str_o[j+3]=0;break;
case '5':str_o[j]=0, str_o[j+1]=1, str_o[j+2]=0, str_o[j+3]=1;break;
case '6':str_o[j]=0, str_o[j+1]=1, str_o[j+2]=1, str_o[j+3]=0;break;
case '7':str_o[j]=0, str_o[j+1]=1, str_o[j+2]=1, str_o[j+3]=1;break;
case '8':str_o[j]=1, str_o[j+1]=0, str_o[j+2]=0, str_o[j+3]=0;break;
case '9':str_o[j]=1, str_o[j+1]=0, str_o[j+2]=0, str_o[j+3]=1;break;
case 'A':str_o[j]=1, str_o[j+1]=0, str_o[j+2]=1, str_o[j+3]=0;break;
case 'B':str_o[j]=1, str_o[j+1]=0, str_o[j+2]=1, str_o[j+3]=1;break;
case 'C':str_o[j]=1, str_o[j+1]=1, str_o[j+2]=0, str_o[j+3]=0;break;
case 'D':str_o[j]=1, str_o[j+1]=1, str_o[j+2]=0, str_o[j+3]=1;break;
case 'E':str_o[j]=1, str_o[j+1]=1, str_o[j+2]=1, str_o[j+3]=0;break;
case 'F':str_o[j]=1, str_o[j+1]=1, str_o[j+2]=1, str_o[j+3]=1;break;
default:break;
}
j += 4; //用变量j实时记录当前字串长度,便于switch工作
}
int flag = 0; //这里插旗,判断第一个不是0的数出现
//for(i=0;i<strlen(str_o)-2;i+=3) 不调用strlen()函数,直接使用j进行计数
for(i=0;i<j-2;i+=3)
{
//int res = 4*(str_o[i]-'0') + 2*(str_o[i+1]-'0') + (str_o[i+2]-'0');
int res = 4*str_o[i] + 2*str_o[i+1] + str_o[i+2]; //二进制向八进制转化
if(res != 0) flag=1; //第一位不是0的数出现,旗子判定可以开始输出
if(flag == 1) printf("%d", res); //从第一位不是0的数开始输出
}
printf("\n");
}
return 0;
}
把这省下一大片时间的代码再交一遍,成功通过!