蓝桥基础练习 || 十六进制转八进制(C语言)

该博客讨论了一个关于从十六进制到八进制转换的编程问题,涉及内存和时间限制。博主首先尝试使用C语言自带的占位符转换,但由于数据范围超出longlongint类型导致错误。接着,博主提出将十六进制数转化为二进制字符串,再从二进制转八进制的解决方案,但初次实现时因循环和计算效率问题导致超时。最后,通过对代码进行优化,如使用数组代替字符串和避免不必要的函数调用,成功解决了问题并实现了正确的转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

资源限制
  内存限制: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; 
}

  把这省下一大片时间的代码再交一遍,成功通过!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Енох-燚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值