累加数
难度:
标签:
题目描述
累加数 是一个字符串,组成它的数字可以形成累加序列。
一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和。
给你一个只包含数字 '0'-'9'
的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回 true
;否则,返回 false
。
说明:累加序列里的数,除数字 0 之外,不会 以 0 开头,所以不会出现 1, 2, 03
或者 1, 02, 3
的情况。
示例 1:
输入:"112358"
输出:true 解释:累加序列为:1, 1, 2, 3, 5, 8
。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8
示例 2:
输入:
"199100199"
输出:true 解释:累加序列为:1, 99, 100, 199。
1 + 99 = 100, 99 + 100 = 199
提示:
1 <= num.length <= 35
num
仅由数字(0
-9
)组成
进阶:你计划如何处理由过大的整数输入导致的溢出?
代码结果
运行时间: 19 ms, 内存: 16.1 MB
/*
* 题目思路:
* 使用Java Stream API的解法较为复杂,原因在于累加数的验证过程涉及递归。我们需要处理字符串分割、转换和校验的过程。
* 我们首先尝试用流的方式来获取所有可能的第一个和第二个数字,然后检查剩余部分是否能形成累加序列。
*/
import java.util.stream.IntStream;
public class AdditiveNumberStream {
public boolean isAdditiveNumber(String num) {
int n = num.length();
return IntStream.range(1, n / 2 + 1)
.flatMap(i -> IntStream.range(i + 1, n)
.filter(j -> isValid(num, i, j)))
.findAny()
.isPresent();
}
private boolean isValid(String num, int i, int j) {
if (num.charAt(0) == '0' && i > 1) return false;
if (num.charAt(i) == '0' && j - i > 1) return false;
String first = num.substring(0, i);
String second = num.substring(i, j);
StringBuilder sb = new StringBuilder(first).append(second);
while (sb.length() < num.length()) {
String sum = addStrings(first, second);
sb.append(sum);
first = second;
second = sum;
}
return sb.toString().equals(num);
}
private String addStrings(String num1, String num2) {
StringBuilder sb = new StringBuilder();
int carry = 0, i = num1.length() - 1, j = num2.length() - 1;
while (i >= 0 || j >= 0 || carry != 0) {
int x = i >= 0 ? num1.charAt(i--) - '0' : 0;
int y = j >= 0 ? num2.charAt(j--) - '0' : 0;
int sum = x + y + carry;
sb.append(sum % 10);
carry = sum / 10;
}
return sb.reverse().toString();
}
}
解释
方法:
这个题解的思路是暴力搜索所有可能的累加数组合。具体来说:
1. 首先判断字符串长度是否小于3,如果是就直接返回False,因为累加序列至少要有3个数。
2. 然后用两个循环来枚举前两个数a和b。第一个数a的范围是[0,length//2],第二个数b的范围是[i+1,length],其中i是a的结束位置。
3. 在枚举a和b时,如果a或b以0开头且不是0本身,就跳过。
4. 对于每一对a和b,调用辅助函数my_check来检查以a和b开头能否形成有效的累加序列。
5. my_check函数递归地计算a+b,将结果与原字符串的剩余部分比较,如果吻合就继续递归检查b和a+b,否则返回False。
6. 如果找到任何一个有效的a和b组合,就返回True,否则在所有组合都检查完后返回False。
时间复杂度:
O(n^4)
空间复杂度:
O(n)
代码细节讲解
🦆
在算法中,如何确定第一个数a的取值范围为[0, length//2]?这个范围是否可能导致错过某些有效的累加数序列?
▷🦆
题解提到当遇到数字以'0'开头时会特别处理,为什么对以0开头的情况进行跳过或特殊处理?是否可能因此漏掉符合条件的累加数?
▷🦆
my_check函数中,如何确保每次递归调用都能正确匹配字符串的剩余部分?是否有可能在某些情况下返回错误的匹配结果?
▷🦆
在实现中,若输入字符串的长度特别长,递归深度是否会对性能或者系统资源造成影响?是否有方法可以避免过深的递归?
▷相关问题
将数组拆分成斐波那契序列
给定一个数字字符串 num
,比如 "123456579"
,我们可以将它分成「斐波那契式」的序列 [123, 456, 579]
。
形式上,斐波那契式 序列是一个非负整数列表 f
,且满足:
0 <= f[i] < 231
,(也就是说,每个整数都符合 32 位 有符号整数类型)f.length >= 3
- 对于所有的
0 <= i < f.length - 2
,都有f[i] + f[i + 1] = f[i + 2]
另外,请注意,将字符串拆分成小块时,每个块的数字一定不要以零开头,除非这个块是数字 0
本身。
返回从 num
拆分出来的任意一组斐波那契式的序列块,如果不能拆分则返回 []
。
示例 1:
输入:num = "1101111" 输出:[11,0,11,11] 解释:输出[110,1,111]也可以。
示例 2:
输入: num = "112358130" 输出: [] 解释: 无法拆分。
示例 3:
输入:"0123" 输出:[] 解释:每个块的数字不能以零开头,因此 "01","2","3" 不是有效答案。
提示:
1 <= num.length <= 200
num
中只含有数字