蓝桥杯:十六进制转八进制的高效算法

总算是在提交了11次都错误之后,第12次过了。也是无语。现在的算法,按照蓝桥杯系统给的10个测试数,显示耗时31ms,内存占用3.589MB。

问题描述
  给定n个十六进制正整数,输出它们对应的八进制数。


输入格式
  输入的第一行为一个正整数n (1<=n<=10)。
  接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。


输出格式
  输出n行,每行为输入对应的八进制正整数。


  【注意】
  输入的十六进制数不会有前导0,比如012A。
  输出的八进制数也不能有前导0。


样例输入
  2
  39
  123ABC


样例输出
  71
  4435274


  【提示】
  先将十六进制数转换成某进制数,再由某进制数转换成八进制。

一开始觉得不难,不过反复写代码都错,我也是醉了。结果看了一下它的测试数据,说好的【每个十六进制数长度不超过100000位】,还真是用尽了,最后两个测试数真的就是100000位。几次都没对就看了一下锦囊:先转成2进制,再转成8进制。恩,确实,100000位的十六进制数就算是long long都超了,只能是按照字符串处理。那好,就先转成2进制,再转8进制,一开始我是这样做的:



看到两边的双引号了吗?我是想告诉大家,我一开始的想法是:所有的过程都是以字符串形式的。每个16进制数(字符串形式)转成4位二进制数(字符串形式)。第二第三行在内存上是一样的,只是在读取的时候第二行是从右往左每4位是一起是一个16进制数;而第三行在读取得时候是从右往左每三位是一个8进制数。第二行(在内存上也是第三行)也是每一位是一个字符存储起来的字符串。

结果很难处理,字符串从后往前遍历很难看,十六进制数'0'~'9'和'A'~'F'(注意有单引号,都是字符),要用switch映射成“0000"~”1111“(注意有双引号,是字符串)。直接这样实现的也不是没有,正如我找到的这篇:http://www.tuicool.com/articles/22I3Ib

但是这样的实现确实麻烦,关键是二进制字符串占用内存很大,switch映射还要考虑补位,因为假设原来的十六进制数时14位,那么前12位是48位二进制数,也就是16个8进制数,但是剩下的两个十六进制数是8位二进制数,要变成8进制数要补一个二进制位(在字符串前面加个'0'),这种就是补位。

有没有更好的实现方法呢?也是看图,这是我现在的实现方法:


这幅图简明地表达了我的实现流程。相比起一个十六进制数4位二进制数,3位二进制数是一位8进制数;我决定选择其最小公倍数12,就可以少考虑一些位数问题。例如我选择2位十六进制数一起处理,那么就是8位二进制数,这样我在转换了2个八进制数之后就会多2个二进制位,留给下次?那不是自寻烦恼吗?!所以如图第一行,我将字符串从右往左一次取出三位,然后注意第二行标号A,类型是int了,可以将每一位用atoi求出对应的int数值,然后注意是乘16,因为这些都是16进制数。因为3位十六进制数,正好是12位二进制数,正好是4位8进制数,就没有了多出一些二进制位留下次的问题。而且一次处理3为十六进制数之后,num就可以重用,就不需要将整个十六进制数的二进制数保存下来了。

转成int,也就是数值类型,怎么转8进制呢?这时候就要想一下,整数在内存中是以二进制方式存在的,也就是说int类型的数据本身就是二进制数,那么要进行与二进制数相关的操作,当然是位运算了,所以有了第三行标号B的语句。这里我每一次都是将num跟7进行位与,7正好是111B,将num的低3位取了出来,而高位的都因为与运算归0了,然后就是怎么把数字变回相应的字符,因为八进制数是在'0'~'7'之间,而ASCII码的数字是连续编号的,所以加'0'(实际上是加了'0'的ASCII码),再转成char类型就是对应的字符了。然后是第四行,num自己右移3位,也就是说将刚才用过的最低3位抛弃了,然后是次低的三位变成最低3为,再进行步骤B,显然步骤B是执行4次,都说了多少遍3个十六进制数是对应4个八进制数。

处理完3位十六进制数,再往前处理三位,直到处理完。而每三位处理,无论前面多出来1位十六进制数还是2位十六进制数,他们转成int之后都是占用4个字节,占用32位(32位二进制数),而高位没有的本来就是0,何必自己劳神费心去补呢?

要注意一点,由于是从右往左处理的,最后不能直接输出数据,要把字符串反过来,就有了这段代码:

for(;j>=0;j--)
{
	Oct[i] += tmpOct[j];
}

最后贴出成品的代码:

#include <iostream>
#include <string>
#include <math.h>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) {
	int n=0;
	cin>> n;
		
	string* Hex = new string[n];
	string tmpOct;
	string* Oct = new string[n];
	
	for(int i=0;i<n;i++)
	{
		int CurBit = 0;
		cin>>Hex[i];
		
		tmpOct = "";
		Oct[i] = "";
		
		for(int j=Hex[i].size()-3;j>=0;j-=3)
		{
			int d = 0;
			for(int k=0;k<3;k++)
			{
				int t = j+k;
				// 16 To 10
				if(Hex[i][t]>='0' && Hex[i][t]<='9')
				{
					CurBit = Hex[i][t]-'0';
				}
				if(Hex[i][t]>='A' && Hex[i][t]<='F')
				{
					CurBit = Hex[i][t]-'A'+10;	
				}
				
				d = d * 16 + CurBit;
			}
			
			// 3bit hex to 4bit oct
			int base = 7; // 111B
			for(int k=0;k<4;k++)
			{
				tmpOct += (char)('0' + (d & base));
				d = d >> 3;
			}
			d = 0;
		} 
		
		// last less three
		int rest = Hex[i].size() % 3;
		if(rest != 0)
		{
			int d = 0;
			for(int k=0;k<rest;k++)
			{
				// 16 To 10
				if(Hex[i][k]>='0' && Hex[i][k]<='9')
				{
					CurBit = Hex[i][k]-'0';
				}
				if(Hex[i][k]>='A' && Hex[i][k]<='F')
				{
					CurBit = Hex[i][k]-'A'+10;	
				}
				
				d = d * 16 + CurBit;
			}
			
			int base = 7; // 111B
			int max = ceil(4.0 / 3.0 * rest);
			// 1bit hex = 4/3 bit oct
			for(int k=0;k<max;k++)
			{
				if(((k==max-1) && (d & base)!=0) || k<max-1)
					tmpOct += char('0' + (d & base));
				d = d >> 3;
			}
		}
		
		int j=tmpOct.size()-1;
		// turn order
		for(;j>=0;j--)
		{
			Oct[i] += tmpOct[j];
		}
	}
	
	for(int i=0;i<n;i++)
	{
		cout<<Oct[i]<<endl;
	}
	
	cin>>n;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/JiLuoXingRen/article/details/50478759