842. 将数组拆分成斐波那契序列
题目来源:LeetCode(力扣)https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/
题目
给定一个数字字符串 S
,比如 S = "123456579"
,我们可以将它分成斐波那契式的序列 [123, 456, 579]
。
形式上,斐波那契式序列是一个非负整数列表 F
,且满足:
0 <= F[i] <= 2^31 - 1
,(也就是说,每个整数都符合 32 位有符号整数类型);F.length >= 3
;- 对于所有的
0 <= i < F.length - 2
,都有F[i] + F[i+1] = F[i+2]
成立。
另外,请注意,将字符串拆分成小块时,每个块的数字一定不要以零开头,除非这个块是数字 0 本身。
返回从 S
拆分出来的任意一组斐波那契式的序列块,如果不能拆分则返回 []
。
示例 1:
输入:"123456579"
输出:[123,456,579]
示例 2:
输入: "11235813"
输出: [1,1,2,3,5,8,13]
示例 3:
输入: "112358130"
输出: []
解释: 这项任务无法完成。
示例 4:
输入:"0123"
输出:[]
解释:每个块的数字不能以零开头,因此 "01","2","3" 不是有效答案。
示例 5:
输入: "1101111"
输出: [110, 1, 111]
解释: 输出 [11,0,11,11] 也同样被接受。
提示:
1 <= S.length <= 200
- 字符串
S
中只含有数字。
解题思路
思路:回溯
先审题,题目给定一个数字字符串 S S S,要求将 S S S 分成斐波那契式的序列。
其中斐波那契式的序列是一个非负整数列表 F F F,且满足以下条件:
- 0 ≤ F [ i ] ≤ 2 31 − 1 0 \leq F[i] \leq 2^{31}-1 0≤F[i]≤231−1;
- F . l e n g t h ≥ 3 F.length \geq 3 F.length≥3;
- 对于所有的 i i i( 0 ≤ i ≤ F . l e n g t h − 2 0 \leq i \leq F.length-2 0≤i≤F.length−2),都有 F [ i ] + F [ i + 1 ] = F [ i + 2 ] F[i]+F[i+1]=F[i+2] F[i]+F[i+1]=F[i+2]。
同时还需要注意:
- 字符串拆分时,拆分后的数字不能含有前导零,除非该数字是 0 本身。
如果字符串
S
S
S 无法拆分成斐波那契式的序列,那么最终返回 []
。
在这里,将 S S S 进行拆分需要不断尝试,我们可以使用回溯的算法来进行实现。
这里先说下大概的思路:
- 创建空列表,用以存储拆分后的数值;
- 遍历字符串,开始进行拆分。根据前面斐波那契式序列的第 3 条件,当拆分第 3 个数字开始,需要进行判断拆分的数字是否等于前两个数字之和。当符合条件时,进行拆分,否则继续寻找。
- 直到字符串 S S S 都被拆分完毕,返回结果。字符串拆分结束后,得到的序列长度应该大于或等于 3,否则返回空列表。
在遍历字符串进行拆分时,这里可以进行适当的剪枝。
- 拆分数字不能含有前导零(除非数字为 0 本身),那么当拆分的数字是以 0 开头时,这里不能拆分长度大于 1 的数字;
- 拆分的数字大小必须是落在区间 [ 0 , 2 31 − 1 ] [0, 2^{31}-1] [0,231−1],如果拆分的数字大于 2 31 − 1 2^{31}-1 231−1,这里不符合要求,进行剪枝;
- 如果序列中长度至少为 2 时,此时拆分的数字大于前两个数字和时,不满足序列第 3 个条件,进行剪枝。
具体的代码实现如下。
class Solution:
def splitIntoFibonacci(self, S: str) -> List[int]:
ans = []
def backtrack(idx):
"""拆分字符串
Args:
idx: 字符串开始索引位置
"""
# 字符串拆分完毕后,需要对序列长度进行一次判断
if idx == len(S):
return len(ans) >= 3
# cur 记录当前拆分数字
cur = 0
# 遍历字符串,开始拆分
for i in range(idx, len(S)):
# 拆分数字不能含有前导零
if S[idx] == '0' and i > idx:
break
cur = cur * 10 + (ord(S[i]) - ord('0'))
# 拆分数字不能大于 2^31 -1
if cur > (1 << 31) - 1:
break
length = len(ans)
# 序列长度至少为 2 时,如果拆分数字大于前面两数之和,剪枝
if length >= 2 and cur > ans[-1] + ans[-2]:
break
# 排除前面的情况时,尝试添加拆分数字到序列中
if length < 2 or cur == ans[-1] + ans[-2]:
ans.append(cur)
# 继续向下找,找到返回 True
if backtrack(i+1):
return True
# 否则回溯,重新拆分
ans.pop()
return False
backtrack(0)
return ans
欢迎关注
公众号 【书所集录】
如有错误,烦请指出,欢迎指点交流。