问题描述:
已知:位数为K(0 < K <200)的N进制数P(2 ≤ N ≤62),该数由0-9, A-Z, a-z组成,其中A-Z代表10-35,a-z代表36-61
求:转换为M进制(2 ≤ M ≤62)后的新数Q
输入:第1行为case数T;第2至第T+1行为T个case,每行输入均为N、M以及P,用空格分隔
输出:采用以下形式输出
N P
M Q
空行
……
Sample Input:
1
62 2 abcdefghiz
Sample Output:
62 abcdefghiz
2 11011100000100010111110010010110011111001001100011010010001
在1220的讨论区中,盛传着一位大神的精简代码,其核心部分就是:
for(l=0;k;){
for(i=k;1<i--;){
t[i-1]+=t[i]%b*a;
t[i]/=b;
}
A[l++]=t[0]%b;
t[0]/=b;
for(;0<k&&!t[k-1];k--);
}
要看懂这段代码,还是必须从最基本的算法推导开始。我们最熟悉的是十进制转化为其他进制的过程,如将29转化成2进制数的过程:
商 余数
(1) 29/2 14 1
(2) 14/2 7 0
(3) 7/2 3 1
(4) 3/2 1 1
(5) 1/2 0 1
29的二进制为:11101,注意是余数的倒序。
我们将其推广到更普通的情形,网上用的一个例子是将二进制转化为八进制:1010011——123,将二进制转为八进制,还是使用相除取余的方法,这一次除数为8。
商 余数
第一轮: (0*2+1)/8 0 1
(1*2+0)/8 00 2
(2*2+1)/8 000 5
(5*2+0)/8 0001 2
(2*2+0)/8 00010 4
(4*2+1)/8 000101 1
(1*2+1)/8 0001010 3
第一轮的过程如此麻烦,它的整个过程实际上是将1010011除以8,将8转化为2进制(1000)在纸上进行推算整个过程会更清晰一些,等到的商为1010,余数为3,得到的余数3即是八进制的最后一位上的数。
第二轮:继续对上一轮所取得的商1010进行除8:
商 余数
(0*2+1)/8 0 1
(1*2+0)/8 0 2
(2*2+1)/8 0 5
(5*2+0)/8 1 2
同样最后所得的余数2为对应八进制数的倒数第二位。
第三轮:对1除8,所得余数为1,对应八进制的第一位为1,而所得的商为0,结束,于是对应的八进制为123。
根据对更普遍的情况的整个过程有了初步的了解,可以进行代码的编写了。
#include <stdio.h>
#include <string.h>
int search(char c);
char num[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int main()
{
int i,j,k,a,b,count,length,lt;
char A[256],B[256];
int buffer[256],s[256];
scanf("%d",&count);
for(i=0;i<count;i++)
{
scanf("%d %d %s",&a,&b,A);
length=strlen(A);
for(j=1;j<=length;j++)
s[j]=search(A[j-1]);
s[0]=0;
k=1;
lt=0;
while(1)
{
for(;k<=length;k++)
{
s[k]+=(s[k-1]%b)*a;
s[k-1]/=b;
}
buffer[lt++]=s[k-1]%b; //得到的B是倒序的
s[k-1]/=b;
k=0;
while(s[k]==0 && k<=length)
k++;
if(k==length+1)
break;
}
for(k=0;k<lt;k++)
{
B[k]=num[buffer[lt-1-k]];
}
B[k]='\0';
printf("%d %s\n",a,A);
printf("%d %s\n",b,B);
printf("\n");
}
return 0;
}
int search(char c)
{
int l=0,r=61;
int m;
while(l<=r)
{
m=(l+r)*0.5;
if(c<num[m])
r=m-1;
else if(c==num[m])
return m;
else if(c>num[m])
l=m+1;
}
return -1;
}
需要注意的是,这段代码得到的b进制数是逆序的,需要进行处理才能输出正确的b进制数。