蓝桥杯的填空题、电子学会 C/C++ 考级的算法题、全国青少年信息素养大赛的算法应用赛项,都特别喜欢考「进制转换」------给你一个超长的大数,让你在二进制、八进制、十六进制之间互转,或者让两个不同进制的数相加。这类题看着简单,却藏着字符映射、大数溢出、进位遗忘等一堆坑。今天我们用一道原创题「校园密码学社的进制密信」,把任意 2~36 进制互转和「大数多进制加法」一次讲透,并给出 C++ / Python 双版本与完整考点拆解。
一、题目与项目目标
原创题:校园密码学社的进制密信
社团用一种进制来加密消息。给定:
-
一个由数字
0-9和大写字母A-Z(代表 10~35)组成的字符串s,表示k进制下的一个非负整数(2 ≤ k ≤ 36); -
一个目标进制
b(2 ≤ b ≤ 36)。
请完成两件事:
-
基础任务 :把
s从k进制转换为b进制字符串输出(同样用0-9/A-Z表示)。 -
进阶任务 :再给定另一个
k进制字符串t,求s + t的和,并以k进制字符串输出(注意:两个数可能极长,几百位,普通整数类型装不下)。
样例:
输入
s="FF", k=16, b=2→ 输出11111111输入
s="1010", k=2, b=16→ 输出A输入
s="F", t="1", k=16→ 进阶输出10输入
s="A", t="A", k=16→ 进阶输出14
二、核心考点
- 进制本质 :一个
k进制串d[n-1]...d[0]的值 = Σ di·k^i。 - 字符 ↔ 数值映射 :
'0'~'9'→0~9;'A'~'Z'→10~35。写反是最高频笔误。 - 大数表示 :几百位的数字必须用字符串 存储,不能先转成
int/long long,否则必然溢出。 - 辗转取余 + 逆序收集 :十进制大数转
b进制,靠「反复除以b取余」,余数从低位到高位,最后整体反转。 - 按位加法 + 进位:多进制加法直接对齐末位、逐位相加并进位,比「先转十进制再加」更优雅也更通用。
三、解法拆解
3.1 思路总览
- k 进制 → b 进制:因为进制不同,最稳妥的中间桥梁是「十进制」。
- 第一步:把
k进制串s用「乘k累加」的方式,变成十进制大数字符串(一个能装下任意位数的十进制串)。 - 第二步:把十进制大数字符串用「反复除以
b取余」变成b进制串。 - 两个 k 进制大数相加 :直接对齐末位逐位加,维护进位
carry,循环条件带上|| carry,最后反转。
3.2 C++ 版本(字符串大数,杜绝溢出)
cpp
#include <bits/stdc++.h>
using namespace std;
int val(char c) {
if (c >= '0' && c <= '9') return c - '0';
return toupper(c) - 'A' + 10; // 10~35 用 A~Z
}
char chr(int v) {
if (v < 10) return '0' + v;
return 'A' + (v - 10);
}
// 十进制大数字符串 = 原十进制串 * k + 一位 digit
string mulAdd(const string& dec, int k, int digit) {
string res;
int carry = digit;
for (int i = (int)dec.size() - 1; i >= 0; --i) {
int cur = (dec[i] - '0') * k + carry;
carry = cur / 10;
res.push_back((cur % 10) + '0');
}
while (carry) { res.push_back((carry % 10) + '0'); carry /= 10; }
reverse(res.begin(), res.end());
return res;
}
// k 进制串 -> 十进制字符串(大数,避免 long long 溢出)
string toBase10(const string& s, int k) {
string dec = "0";
for (char c : s) dec = mulAdd(dec, k, val(c));
return dec;
}
// 十进制字符串 -> b 进制字符串(对十进制大数反复除 b 取余)
string base10ToB(string dec, int b) {
if (dec == "0") return "0";
string res;
while (dec != "0") {
int rem = 0;
string quo;
for (char c : dec) {
int cur = rem * 10 + (c - '0');
quo.push_back((cur / b) + '0');
rem = cur % b;
}
size_t p = quo.find_first_not_of('0');
dec = (p == string::npos) ? "0" : quo.substr(p);
res.push_back(chr(rem));
}
reverse(res.begin(), res.end());
return res;
}
// 组合:k 进制 -> b 进制
string kToB(const string& s, int k, int b) {
return base10ToB(toBase10(s, k), b);
}
// 进阶:两个 k 进制大数直接按位相加(推荐,不依赖十进制中间表示)
string addBaseK(string a, string b, int k) {
// 先去掉输入的前导零,避免把「占位 0」当有效数字参与进位
auto strip = [](string& x) {
size_t p = x.find_first_not_of('0');
if (p == string::npos) x = "0"; // 全 0 时保留一个 0
else x = x.substr(p);
};
strip(a); strip(b);
string res;
int i = (int)a.size() - 1, j = (int)b.size() - 1, carry = 0;
while (i >= 0 || j >= 0 || carry) {
int x = (i >= 0) ? val(a[i--]) : 0;
int y = (j >= 0) ? val(b[j--]) : 0;
int s = x + y + carry;
carry = s / k;
res.push_back(chr(s % k));
}
reverse(res.begin(), res.end());
return res;
}
int main() {
cout << kToB("FF", 16, 2) << "\n"; // 11111111
cout << kToB("1010", 2, 16) << "\n"; // A
cout << addBaseK("F", "1", 16) << "\n"; // 10
cout << addBaseK("A", "A", 16) << "\n"; // 14
return 0;
}
3.3 Python 版本(利用无限精度整数,极简)
python
def val(c):
return int(c) if c.isdigit() else ord(c.upper()) - ord('A') + 10
def chr_(v):
return str(v) if v < 10 else chr(ord('A') + v - 10)
def dec_to_base(n, b):
if n == 0:
return "0"
res = []
while n > 0:
res.append(chr_(n % b))
n //= b
return ''.join(reversed(res))
def k_to_b(s, k, b):
return dec_to_base(int(s, k), b) # Python 整型无上限,超大数也稳
def add_basek(a, b, k):
return dec_to_base(int(a, k) + int(b, k), k)
print(k_to_b("FF", 16, 2)) # 11111111
print(k_to_b("1010", 2, 16)) # A
print(add_basek("F", "1", 16)) # 10
print(add_basek("A", "A", 16)) # 14
注意:Python 的
int(s, k)能直接把k进制串转成无限精度整数,所以转换和加法都能「一行搞定」。但蓝桥杯 / 电子学会考级往往要求你手写转换过程,所以 C++ 版的「字符串大数」思路才是真正要掌握的考点。
四、时间 / 空间复杂度
- toBase10 :输入长
L,十进制串长约L·log₁₀k,每位做一遍mulAdd(长度O(L·logk)),整体约O(L²·logk)。 - base10ToB :十进制串长
D≈L·log₁₀k,每次除以b扫描整串O(D),共D次,约O(D²)=O(L²·log²k)。 - kToB 组合 :
O(L²·log²k),足以应付题目里几百位的输入。 - addBaseK :仅
O(max(|a|, |b|)),线性扫描,非常高效。 - 空间 :均只用到与输入长度同阶的临时字符串,
O(L)。
五、六大易错点
- 字母大小写 :输入可能混用
a-f,务必toupper后再映射;输出统一用大写A-Z。 - 字符与数值写反 :
val('A')=10不是 1;chr(10)='A'。写反结果全错。 - 进制上界 36:超过 36 需要更多字符(如小写字母),题目限定 2~36,别越界访问数组。
- 余数要逆序 :「除
b取余」得到的是从低位到高位,最后一定reverse一次,否则得到反序答案。 - long long 溢出 :几百位的数字绝不能 先
stoi/stoll转成整数,必须用字符串大数。这是本题最大的坑。 - 进位漏加 :
addBaseK的循环条件要带|| carry,否则最高位产生的进位会被丢掉,结果少一位。 - 输入前导零 :若输入串本身带前导
0(如0023),必须先在加法里把前导零规范化,否则会把这个「占位 0」当有效数字参与进位,导致结果凭空多一位前导0。
六、进阶拓展
- 大数多进制乘法:与加法同理,按位相乘累加、维护进位,可拓展到任意进制大数乘法。
- 负进制转换 :如
-2进制,要求余数始终落在[0, |b|-1],取余后要「修正」到非负区间,是蓝桥杯偏难怪题。 - 浮点进制 :小数部分用「乘
b取整」不断生成小数位,处理循环节。 - 与高精度结合:C++ 版我们正是用十进制字符串模拟「乘加 / 除法」,这正是高精度算法的核心思想,理解了它,高精度加减乘除都能触类旁通。
- 真题风格 :蓝桥杯填空常给一个超长数让你转成某进制输出,或让两个二进制 / 十六进制数相加,本质就是上面的
kToB与addBaseK。
七、小结与互动
进制转换的本质只有一句话:位权展开 + 辗转取余 + 逆序收集 ;大数场景则用字符串模拟运算避免溢出。把 kToB 和 addBaseK 两个函数记熟,蓝桥杯、电子学会 C/C++ 考级、信息素养大赛的进制题基本都能直接套。
练手留言区 :试试用 addBaseK 计算 s="1Z", t="1", k=36 的结果是多少?(提示:Z 是 35)欢迎在评论区写下你的答案,也欢迎分享你遇到过的进制转换奇葩坑。
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:
- 全国青少年信息素养大赛复赛集训题目Python&C++.docx
https://pan.quark.cn/s/93995d3cb150 - 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
https://pan.quark.cn/s/da97b5dbf75d - Python背记手册.pdf
https://pan.quark.cn/s/7568ae9ca92b - Python课程
https://pan.quark.cn/s/a94bf02d00c6 - 2024信息素养大赛图形化复赛集训题答案3-9
https://pan.quark.cn/s/6ccab7ec3cbc - 2025年03月份电子学会考级真题
https://pan.quark.cn/s/4403c4228912 - 2025全国青少年信息素养大赛赛项说明
https://pan.quark.cn/s/d9d0df4a9f29 - 青少儿信息素养大赛编程资料
https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注获取最新分享。