题目来源:LeetCode306:累加数
问题抽象: 给定一个只包含数字的字符串 num,要求判断该字符串是否可拆分为一个 累加序列(至少包含三个数),满足以下核心需求:
-
累加序列定义:
- 序列结构:字符串可拆分为若干数字序列
[F0, F1, ..., Fk](k ≥ 2); - 累加规则:从第三项开始,每项等于前两项之和(即
F_i = F_{i-2} + F_{i-1},i ≥ 2); - 无前导零:每个数字无多余前导零(如
"01"非法,但"0"合法)。
- 序列结构:字符串可拆分为若干数字序列
-
输入输出:
- 输入:字符串
num(长度1 ≤ len(num) ≤ 35); - 输出:布尔值
true(是累加数)或false(不是累加数)。
- 输入:字符串
-
计算约束:
- 需处理 大整数加法(因数字可能超过
2^{128},需字符串运算); - 时间复杂度 O(n^3)(三重循环枚举前两个数的结束位置);
- 空间复杂度 O(n)(递归栈或字符串存储开销)。
- 需处理 大整数加法(因数字可能超过
-
边界处理:
- 短字符串:长度
<3时返回false(至少需三个数字); - 前导零:如
"0123"中"01"非法(但"0"合法); - 序列长度不均:如
"199100199"拆为[199,100,299](199+100=299); - 大整数加法:如
"121474836472147483648"(需字符串加法验证)。
- 短字符串:长度
-
验证规则:
- 枚举前两个数:通过起始位置
i和j分割出F0和F1(需避免前导零); - 递归/迭代验证:计算
F2 = F0 + F1,检查后续字符串是否以F2开头,并继续验证F3 = F1 + F2等; - 覆盖全字符串:所有字符必须被序列完全使用(无剩余字符)。
- 枚举前两个数:通过起始位置
解题思路
-
枚举前两个数字:
- 第一个数字从字符串开头开始,长度至少为 1,最多为
n - 2(保证后续至少有两个数字)。 - 第二个数字从第一个数字结束位置开始,长度至少为 1,最多为剩余长度减 1(保证后续至少有一个数字)。
- 如果数字以
0开头且长度大于 1,则跳过(非法)。
- 第一个数字从字符串开头开始,长度至少为 1,最多为
-
字符串加法:
- 实现大数加法函数
add(String a, String b),避免整数溢出问题。 - 计算前两个数字的和,生成字符串表示。
- 实现大数加法函数
-
验证后续序列:
- 从第二个数字结束位置开始,检查后续字符串是否以计算的和开头。
- 若匹配成功,更新前两个数字(上一个数字变为当前数字,当前数字变为和),并计算新的和。
- 重复此过程直到字符串结束。若整个字符串匹配成功,返回
true。
-
终止条件:
- 若所有枚举组合均未成功,返回
false。
- 若所有枚举组合均未成功,返回
代码实现(Java版)🔥点击下载源码
class Solution {
public boolean isAdditiveNumber(String num) {
int n = num.length();
// 枚举第一个数字的结束位置(第一个数字范围:[0, i))
for (int i = 1; i <= n - 2; i++) {
// 第一个数字以 '0' 开头且长度大于1,非法(只能为 "0")
if (num.charAt(0) == '0' && i > 1) {
break;
}
String num1 = num.substring(0, i); // 第一个数字
// 枚举第二个数字的结束位置(第二个数字范围:[i, j))
for (int j = i + 1; j <= n - 1; j++) {
// 第二个数字以 '0' 开头且长度大于1,非法(只能为 "0")
if (num.charAt(i) == '0' && j - i > 1) {
break;
}
String num2 = num.substring(i, j); // 第二个数字
String sum = add(num1, num2); // 计算和
// 检查剩余部分是否满足累加序列
if (checkRemaining(num, j, num2, sum)) {
return true;
}
}
}
return false;
}
// 检查剩余字符串是否满足累加序列
private boolean checkRemaining(String num, int start, String prev, String sum) {
int index = start; // 当前匹配位置
String current = sum; // 当前需匹配的和
String prevNum = prev; // 上一个数字
// 遍历剩余字符串
while (index < num.length()) {
// 剩余长度不足
if (index + current.length() > num.length()) {
return false;
}
// 获取子串并匹配
String sub = num.substring(index, index + current.length());
if (!sub.equals(current)) {
return false;
}
index += current.length(); // 更新匹配位置
// 未到字符串末尾,继续生成下一个和
if (index < num.length()) {
String nextSum = add(prevNum, current); // 下一个和
prevNum = current; // 更新上一个数字
current = nextSum; // 更新当前需匹配的和
}
}
return true; // 整个字符串匹配成功
}
// 字符串加法(处理大数溢出)
private String add(String a, String b) {
StringBuilder sb = new StringBuilder();
int i = a.length() - 1, j = b.length() - 1;
int carry = 0; // 进位
// 从低位到高位逐位相加
while (i >= 0 || j >= 0 || carry != 0) {
int x = (i >= 0) ? a.charAt(i--) - '0' : 0; // 字符转数字
int y = (j >= 0) ? b.charAt(j--) - '0' : 0;
int s = x + y + carry; // 当前位和
carry = s / 10; // 更新进位
sb.append(s % 10); // 添加当前位结果
}
return sb.reverse().toString(); // 反转得到最终结果
}
}
代码说明
-
主函数
isAdditiveNumber:- 枚举第一个数字(
num1)的结束位置i(范围[1, n-2])。 - 枚举第二个数字(
num2)的结束位置j(范围[i+1, n-1])。 - 跳过以
0开头且长度大于 1 的数字。 - 调用
checkRemaining验证后续序列。
- 枚举第一个数字(
-
验证函数
checkRemaining:- 从位置
start开始匹配字符串sum。 - 匹配成功后,更新
prevNum(上一个数字)和current(当前需匹配的和)。 - 循环直到字符串结束或匹配失败。
- 从位置
-
字符串加法
add:- 从低位到高位逐位相加,处理进位。
- 使用
StringBuilder构建结果,反转后返回字符串形式,避免整数溢出。
提交详情(执行用时、内存消耗)

2755

被折叠的 条评论
为什么被折叠?



