题目
标题和出处
标题:累加数
出处:306. 累加数
难度
5 级
题目描述
要求
累加数是一个字符串,组成它的数字可以形成累加序列。
一个有效的累加序列必须至少包含 3 \texttt{3} 3 个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和。
给定一个只包含数字的字符串,如果字符串是累加数则返回 true \texttt{true} true,否则返回 false \texttt{false} false。
累加序列里的数不能有前导零,所以 1, 2, 03 \texttt{1, 2, 03} 1, 2, 03 或 1, 02, 3 \texttt{1, 02, 3} 1, 02, 3 都是无效的。
示例
示例 1:
输入:
num
=
"112358"
\texttt{num = "112358"}
num = "112358"
输出:
true
\texttt{true}
true
解释:
累加序列为:
1,
1,
2,
3,
5,
8
\texttt{1, 1, 2, 3, 5, 8}
1, 1, 2, 3, 5, 8。
1
+
1
=
2
\texttt{1} + \texttt{1} = \texttt{2}
1+1=2,
1
+
2
=
3
\texttt{1} + \texttt{2} = \texttt{3}
1+2=3,
2
+
3
=
5
\texttt{2} + \texttt{3} = \texttt{5}
2+3=5,
3
+
5
=
8
\texttt{3} + \texttt{5} = \texttt{8}
3+5=8。
示例 2:
输入:
num
=
"199100199"
\texttt{num = "199100199"}
num = "199100199"
输出:
true
\texttt{true}
true
解释:
累加序列为:
1,
99,
100,
199
\texttt{1, 99, 100, 199}
1, 99, 100, 199。
1
+
99
=
100
\texttt{1} + \texttt{99} = \texttt{100}
1+99=100,
99
+
100
=
199
\texttt{99} + \texttt{100} = \texttt{199}
99+100=199。
数据范围
- 1 ≤ num.length ≤ 35 \texttt{1} \le \texttt{num.length} \le \texttt{35} 1≤num.length≤35
- num \texttt{num} num 仅由数字组成
进阶
你如何处理过大的整数输入导致的溢出?
解法
思路和算法
根据累加序列的定义,只要确定前两个数,即可确定累加序列。由于累加序列至少包含 3 3 3 个数,因此需要枚举前 3 3 3 个数的开始下标,然后判断给定的字符串 num \textit{num} num 是否为累加数。
整个累加序列的第一个数的开始下标是 0 0 0,需要枚举第二个数和第三个数的开始下标判断是否存在累加序列。可以使用回溯判断是否组成累加序列。
回溯过程中需要记录三个数的开始下标 start 1 \textit{start}_1 start1、 start 2 \textit{start}_2 start2 和 start 3 \textit{start}_3 start3,则前两个数的结束下标分别是 start 2 − 1 \textit{start}_2 - 1 start2−1 和 start 3 − 1 \textit{start}_3 - 1 start3−1,长度分别是 start 2 − start 1 \textit{start}_2 - \textit{start}_1 start2−start1 和 start 3 − start 2 \textit{start}_3 - \textit{start}_2 start3−start2。回溯的做法如下。
-
如果前两个数存在长度大于 1 1 1 且最高位是 0 0 0 的情况,则存在前导零,不符合累加序列的要求。
-
如果 start 3 \textit{start}_3 start3 等于字符串 num \textit{num} num 的长度,则到达字符串的末尾且序列至少包含 3 3 3 个数,符合累加序列的要求。
-
计算前两个数的和,根据前两个数的和计算第三个数的长度,并与第三个数比较。
-
如果第三个数的长度大于 1 1 1 且第三个数的最高位是 0 0 0,则存在前导零,不符合累加序列的要求。
-
如果前两个数的和与第三个数不匹配,则不符合累加序列的要求。
-
如果第三个数不存在前导零,且前两个数的和与第三个数匹配,则将第二个数和第三个数作为三个数中的前两个数,继续回溯。
-
如果遇到累加序列,则返回 true \text{true} true。如果回溯结束时仍没有遇到累加序列,则返回 false \text{false} false。
实现方面,由于整数位数可能较多,为了避免溢出,可以使用字符串操作模拟整数加法。
代码
class Solution {
int n;
String num;
public boolean isAdditiveNumber(String num) {
this.n = num.length();
this.num = num;
for (int start2 = 1; start2 < n; start2++) {
for (int start3 = start2 + 1; start3 < n; start3++) {
if (backtrack(0, start2, start3)) {
return true;
}
}
}
return false;
}
public boolean backtrack(int start1, int start2, int start3) {
int length1 = start2 - start1, length2 = start3 - start2;
if (length1 > 1 && num.charAt(start1) == '0' || length2 > 1 && num.charAt(start2) == '0') {
return false;
}
if (start3 == n) {
return true;
}
String sum = addStrings(start1, start2 - 1, start2, start3 - 1);
int length3 = sum.length();
if (length3 > 1 && num.charAt(start3) == '0' || !equals(sum, start3)) {
return false;
}
return backtrack(start2, start3, start3 + length3);
}
public String addStrings(int start1, int end1, int start2, int end2) {
StringBuffer sb = new StringBuffer();
int index1 = end1, index2 = end2;
int carry = 0;
while (index1 >= start1 || index2 >= start2 || carry != 0) {
int digit1 = index1 >= start1 ? num.charAt(index1) - '0' : 0;
int digit2 = index2 >= start2 ? num.charAt(index2) - '0' : 0;
int sum = digit1 + digit2 + carry;
sb.append(sum % 10);
carry = sum / 10;
index1--;
index2--;
}
return sb.reverse().toString();
}
public boolean equals(String sum, int start) {
int sumLength = sum.length();
if (n - start < sumLength) {
return false;
}
for (int i = 0; i < sumLength; i++) {
if (sum.charAt(i) != num.charAt(start + i)) {
return false;
}
}
return true;
}
}
复杂度分析
-
时间复杂度: O ( n 3 ) O(n^3) O(n3),其中 n n n 是字符串 num \textit{num} num 的长度。需要 O ( n 2 ) O(n^2) O(n2) 的时间枚举整个累加序列的第二个数和第三个数的开始下标,对于每个可能的序列需要 O ( n ) O(n) O(n) 的时间判断是否符合累加序列的要求,因此时间复杂度是 O ( n 3 ) O(n^3) O(n3)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 num \textit{num} num 的长度。递归调用栈的层数是 O ( n ) O(n) O(n)。
749

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



