LeetCode 842. 将数组拆分成斐波那契序列 | Python

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. 1 <= S.length <= 200
  2. 字符串 S 中只含有数字。

解题思路


思路:回溯

先审题,题目给定一个数字字符串 S S S,要求将 S S S 分成斐波那契式的序列。

其中斐波那契式的序列是一个非负整数列表 F F F,且满足以下条件:

  • 0 ≤ F [ i ] ≤ 2 31 − 1 0 \leq F[i] \leq 2^{31}-1 0F[i]2311
  • F . l e n g t h ≥ 3 F.length \geq 3 F.length3
  • 对于所有的 i i i 0 ≤ i ≤ F . l e n g t h − 2 0 \leq i \leq F.length-2 0iF.length2),都有 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,2311],如果拆分的数字大于 2 31 − 1 2^{31}-1 2311,这里不符合要求,进行剪枝;
  • 如果序列中长度至少为 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

欢迎关注


公众号 【书所集录


如有错误,烦请指出,欢迎指点交流。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值