Medium
题目描述
给定一个数字字符串 num,比如 "123456579"。我们可以将它拆分成斐波那契数列 [123, 456, 579]。
形式上,斐波那契数列是一个非负整数列表 f,满足:
0 <= f[i] < 2^31(即每个整数都在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:
输入: num = "0123"
输出: []
解释: 不允许前导零,所以 "01", "2", "3" 是无效的。
约束条件:
1 <= num.length <= 200num仅包含数字
解题思路
这是一道典型的回溯问题。我们需要尝试所有可能的拆分方式来构造斐波那契序列。
解题思路:
回溯搜索:我们首先确定前两个数字,然后根据斐波那契性质推导后续数字
边界条件处理:
- 每个数字不能有前导零(除非数字本身是0)
- 每个数字必须在32位整数范围内
- 序列长度至少为3
优化策略:
- 前两个数字的长度有上限:如果第一个数长度为i,第二个数长度为j,那么i+j不能超过字符串长度的一半(因为后续数字会越来越长)
- 一旦确定前两个数字,后续的拆分就是确定性的
具体实现:
- 外层双重循环枚举前两个数字的所有可能长度
- 对于每种组合,使用回溯验证是否能构成完整的斐波那契序列
- 在回溯过程中,每次计算下一个应该出现的数字,然后检查字符串中对应位置是否匹配
推荐解法:回溯 + 剪枝,时间复杂度相对较优,代码清晰易懂。
代码实现
class Solution {
public:
vector<int> splitIntoFibonacci(string num) {
vector<int> result;
backtrack(num, 0, result);
return result;
}
private:
bool backtrack(const string& num, int index, vector<int>& path) {
if (index == num.length()) {
return path.size() >= 3;
}
long long current = 0;
for (int i = index; i < num.length(); i++) {
// 避免前导零
if (i > index && num[index] == '0') break;
current = current * 10 + (num[i] - '0');
// 超出32位整数范围
if (current > INT_MAX) break;
if (path.size() < 2 ||
(long long)path[path.size()-1] + path[path.size()-2] == current) {
path.push_back((int)current);
if (backtrack(num, i + 1, path)) {
return true;
}
path.pop_back();
} else if (path.size() >= 2 &&
(long long)path[path.size()-1] + path[path.size()-2] < current) {
break; // 当前数已经太大,后续不可能匹配
}
}
return false;
}
};
class Solution:
def splitIntoFibonacci(self, num: str) -> List[int]:
def backtrack(index, path):
if index == len(num):
return len(path) >= 3
current = 0
for i in range(index, len(num)):
# 避免前导零
if i > index and num[index] == '0':
break
current = current * 10 + int(num[i])
# 超出32位整数范围
if current > 2**31 - 1:
break
if len(path) < 2 or path[-1] + path[-2] == current:
path.append(current)
if backtrack(i + 1, path):
return True
path.pop()
elif len(path) >= 2 and path[-1] + path[-2] < current:
break # 当前数已经太大,后续不可能匹配
return False
result = []
backtrack(0, result)
return result
public class Solution {
public IList<int> SplitIntoFibonacci(string num) {
var result = new List<int>();
if (Backtrack(num, 0, result)) {
return result;
}
return new List<int>();
}
private bool Backtrack(string num, int index, List<int> path) {
if (index == num.Length) {
return path.Count >= 3;
}
long current = 0;
for (int i = index; i < num.Length; i++) {
// 避免前导零
if (i > index && num[index] == '0') break;
current = current * 10 + (num[i] - '0');
// 超出32位整数范围
if (current > int.MaxValue) break;
if (path.Count < 2 ||
(long)path[path.Count-1] + path[path.Count-2] == current) {
path.Add((int)current);
if (Backtrack(num, i + 1, path)) {
return true;
}
path.RemoveAt(path.Count - 1);
} else if (path.Count >= 2 &&
(long)path[path.Count-1] + path[path.Count-2] < current) {
break; // 当前数已经太大,后续不可能匹配
}
}
return false;
}
}
var splitIntoFibonacci = function(num) {
const result = [];
function backtrack(index, sequence) {
if (index === num.length) {
return sequence.length >= 3;
}
for (let i = index; i < num.length; i++) {
const substr = num.slice(index, i + 1);
if (substr.length > 1 && substr[0] === '0') {
break;
}
const numVal = parseInt(substr);
if (numVal > Math.pow(2, 31) - 1) {
break;
}
if (sequence.length < 2) {
sequence.push(numVal);
if (backtrack(i + 1, sequence)) {
return true;
}
sequence.pop();
} else {
const expectedSum = sequence[sequence.length - 1] + sequence[sequence.length - 2];
if (numVal === expectedSum) {
sequence.push(numVal);
if (backtrack(i + 1, sequence)) {
return true;
}
sequence.pop();
} else if (numVal > expectedSum) {
break;
}
}
}
return false;
}
if (backtrack(0, result)) {
return result;
}
return [];
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(n²) |
| 空间复杂度 | O(n) |
说明:
- 时间复杂度:在最坏情况下,我们需要尝试所有可能的前两个数字的组合,这大约是O(n²)种可能。对于每种组合,验证过程是O(n)
- 空间复杂度:主要是递归调用栈和结果数组的空间,最深递归深度为O(n)
相关题目
. Additive Number (Medium)
. Fibonacci Number (Easy)