卡普列加数是平方后能在中间合法拆分为两个非空部分且和等于原数的正整数;最常错的边界条件是忽略右段不能有前导零(如“00”“001”非法)、1为特例需单独处理,以及切分必须严格在中间(不能全左或全右)。

什么是卡普列加数,以及判断时最常错的边界条件
卡普列加数不是“平方后拆开相加等于原数”这么简单——它要求拆分后的两部分都必须是**非空十进制数字串**,且**右边部分不能全为 0**(即不允许 00、000 这类),但允许左边为 0(如 9 的平方是 81,拆成 8 和 1,不是卡普列加数;而 297 的平方是 88209,可拆为 88 + 209 = 297)。
最容易踩的坑是:把 1、9、45 等小数字直接当成卡普列加数,却忽略了定义中对“拆分位置”的严格限制——必须在平方结果的**某处中间切一刀**(不能全切到左边或右边),且两段都得是合法整数表示(即右段不能以 0 开头,除非它本身就是 0,但 0 不被接受,因为原数是正整数)。
-
1:平方是1,只有一位,无法拆成两个非空部分 → 不是卡普列加数 -
9:平方是81,只能拆成8+1=9→ 是卡普列加数 -
10:平方是100,可拆为1+00(非法,右段含前导零)、10+0(右段是0,不被接受)→ 不是卡普列加数
用 std::to_string 和字符串切片实现安全拆分
不要用除法和取模硬算位数,容易溢出且逻辑绕。直接转字符串,遍历所有合法切点(从第 1 位后开始,到倒数第 1 位前结束),再用 std::stoll 转回整数做加法验证。
关键点在于:右段字符串若以 '0' 开头(长度 > 1),直接跳过该切点——这是排除 00、01 等非法右段的最快方式。
bool isKaprekar(long long n) {
if (n == 1) return true; // 特例:定义允许 1 是卡普列加数(尽管它平方后无法拆,但数学界约定俗成)
long long sq = n * n;
std::string s = std::to_string(sq);
int len = s.length();
for (int i = 1; i 0)
if (right.length() > 1 && right[0] == '0') continue;
long long l = std::stoll(left);
long long r = std::stoll(right);
if (l + r == n) return true;
}
return false;
}
long long 为什么比 int 更稳妥
卡普列加数会快速变大:703² = 494209,2223² = 4941729,已超出 int 范围(通常为 ±2³¹−1 ≈ 21 亿)。用 int 算平方会静默溢出,导致字符串转出错误值。
- 输入
n = 2223,int下n*n可能变成负数或随机值 -
std::stoll支持最多 19 位十进制数,足够覆盖10⁹以内所有卡普列加数的平方 - 如果要支持更大的数(如
10¹⁸),需改用__int128(GCC)或字符串乘法,但绝大多数实际场景用long long就够了
测试时别漏掉 1 和 99999 这类边界值
很多实现把 1 判为 false,但标准定义(OEIS A006886)明确包含 1;而像 99999 这种接近 10⁵ 的数,平方是 9999800001(10 位),必须确保字符串切分循环跑满 i = 1 到 9,且 stoll 不因超长失败(9999800001 在 long long 范围内)。
建议用已知序列验证:1, 9, 45, 55, 99, 297, 703, 999, 2223, 2728, 4879, 4950, 5050, 7272, 9999, 17344, 22222, 38962, 77778, 82656, 95121, 99999……其中 99999 对应的平方是 9999800001,拆成 99998 + 00001 是错的(右段前导零),但 999980 + 0001 也不合法;真正成立的是 99998 + 00001?不对——正确拆法是 9999800001 → 99998 + 00001 无效,实际是 999980 + 0001?仍无效。事实上 99999 成立靠的是 9999800001 拆成 99998 + 00001?不,标准验证是 99998 + 00001 = 99999,但右段 "00001" 在代码里会被 stoll 转成 1,而字符串检查应拒绝它——所以你的代码必须在调用 stoll 前判断右段是否含前导零且长度 > 1。
这个细节恰恰是最容易被忽略的:字符串切出来是 "00001",你没拦住就交给 stoll,它默默转成 1,结果算对了,但违反了卡普列加数定义(右段应保持原始位数语义)。真正合规的做法是——要么要求右段无前导零,要么按原始位数解释(即不允许任何前导零)。主流实现采用前者。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











