您的位置:首页 > 其它

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

2016-01-07 21:21 351 查看
总算是在提交了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
;
string tmpOct;
string* Oct = new string
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: