本题要求将一个非负整数 N 转换成一种“变长编码”。这种编码的核心思想是:用尽可能少的字节表示较小的数,同时也能灵活地表示大数。
题目给出的编码规则可以概括为三步:
最后,将这若干个字节用十六进制形式输出,字节之间用空格隔开。
举个例子:
0,补足7位得到 0000000。00000000,即 00。1110011110(共10位)。0011110,高3位补4个0变成 0000111。10011110(即 0x9E)。00000111(即 0x07)。9E 07。题目要求 0≤N≤1018,所以 N 最大不会超过 260,对应的组数最多为 60/7≈9 组,完全可以用数组存储。
根据规则,我们可以模拟整个过程:
n & 0x7F 得到),然后将 N 右移 7 位,继续提取下一组,直到 N 变为 0。| 0x80)。对于 N=0 的情况,按照规则也会输出一个字节 00,需要特殊处理。
我们采用数组(或向量)来存储拆分出的 7 位组。算法流程如下:
00 并结束。N & 0x7F 存入数组。N >>= 7,继续循环。l-1 个元素(除了最后一组),把它们与 0x80 做按位或运算,给最高位填 1。(s >> 4) & 0x0F。s & 0x0F。'0''9',1015 转为 'A'~'F'。每输出一个字节前,如果不是第一个字节,先输出一个空格。
下面是题目中给出的 C++ 参考代码,并辅以详细注释:
cpp1#include <iostream> 2using namespace std; 3 4// 输出4位对应的十六进制字符 5void output_digit(int d) { 6 if (d >= 10) 7 cout << (char)('A' + d - 10); // 10→A, 11→B ... 15→F 8 else 9 cout << (char)('0' + d); // 0→0 ... 9→9 10} 11 12// 输出一个字节的两位十六进制 13void output_code(int s) { 14 output_digit(s >> 4); // 高4位 15 output_digit(s & 0x0f); // 低4位 16} 17 18int main() { 19 long long n = 0; 20 cin >> n; 21 22 // 特判 n == 0 的情况(原代码未处理,这里需要补充) 23 if (n == 0) { 24 cout << "00" << endl; 25 return 0; 26 } 27 28 int split[10]; // 最多 10 组,足够 29 int l = 0; // 组的个数 30 31 // 从低位到高位,每7位分成一组 32 while (n > 0) { 33 split[l] = (int)(n & 0x7f); // 取最低的7位 34 n >>= 7; // 右移7位 35 l++; 36 } 37 38 // 除了最后一组(最高组),其他组最高位填 1 39 for (int i = 0; i < l - 1; i++) 40 split[i] |= 0x80; 41 42 // 按顺序输出(低组到高组) 43 output_code(split[0]); 44 for (int i = 1; i < l; i++) { 45 cout << " "; 46 output_code(split[i]); 47 } 48 cout << endl; 49 return 0; 50}
代码解释:
output_digit 将 0~15 的值转为十六进制字符输出。output_code 将一个字节拆成高4位和低4位,分别调用 output_digit 输出。split 数组用来按顺序存储每一组。l 记录组数。0x7f(二进制 0111 1111),右移7位相当于“扔掉”已经处理过的低位。split[0] 是最低7位组,split[l-1] 是最高7位组。0 到 l-2 的组最高位设置为1(|= 0x80,0x80 即 1000 0000)。注意:原参考代码未处理 n=0 的特殊情况。若不处理,while 循环一次都不执行,l 为 0,后面访问 split 数组会出现越界或输出异常。因此实际编写时需要加入 n == 0 的判断。
每一轮循环将 N 右移 7 位。当 N 最大为 1018≈260 时,最多循环 ⌈60/7⌉=9 次。因此循环次数是一个很小的常数(与输入规模的对数相关)。
输出时每个字节调用两次 output_digit,组数最多同样为 9,因此总体时间复杂度为 O(logN),在本题中可以看作 O(1)。空间复杂度也只需要一个很小的数组,为 O(1)。
本题主要考察位运算和进制转换的基本功。只要理解了“每7位分组,除最后一组外最高位填1”这一规则,剩下的就是用掩码和移位操作来模拟整个过程了。需要留意的细节是十六进制的大写输出,以及 N=0 的边界处理。