题目原文
842. 将数组拆分成斐波那契序列
给定一个数字字符串 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
中只含有数字。
尝试解答
python在int转换大于2^31-1的数值的时候竟然不会报错!python天下第一!
1 class Solution: 2 def splitIntoFibonacci(self, S): 3 ans = list() 4 def backTrack(index): 5 if index==len(S): 6 return len(ans)>=3 7 cur = 0 8 for i in range(index,len(S)): 9 if i>index and S[index]=="0": 10 break 11 cur = cur*10+int(S[i]) 12 if cur>(2**31)-1: 13 break 14 if len(ans)<2 or ans[-2]+ans[-1]==cur: 15 ans.append(cur) 16 if backTrack(i+1): 17 return True 18 ans.pop(-1) 19 elif len(ans)>=2 and ans[-2]+ans[-1]==cur: 20 break 21 return False 22 if backTrack(0): 23 return ans 24 else: 25 return list()
标准题解
这道题漂亮的解法是点到为止的传统功夫——递归回溯下的DFS。
回溯 + 剪枝
将给定的字符串拆分成斐波那契式序列,可以通过回溯的方法实现。
使用列表存储拆分出的数,回溯过程中维护该列表的元素,列表初始为空。遍历字符串的所有可能的前缀,作为当前被拆分出的数,然后对剩余部分继续拆分,直到整个字符串拆分完毕。
根据斐波那契式序列的要求,从第 333 个数开始,每个数都等于前 222 个数的和,因此从第 333 个数开始,需要判断拆分出的数是否等于前 222 个数的和,只有满足要求时才进行拆分,否则不进行拆分。
回溯过程中,还有三处可以进行剪枝操作。
拆分出的数如果不是 000,则不能以 000 开头,因此如果字符串剩下的部分以 000 开头,就不需要考虑拆分出长度大于 111 的数,因为长度大于 111 的数以 000 开头是不符合要求的,不可能继续拆分得到斐波那契式序列;
拆分出的数必须符合 323232 位有符号整数类型,即每个数必须在 [0,231−1][0,2^{31}-1][0,231−1] 的范围内,如果拆分出的数大于 231−12^{31}-1231−1,则不符合要求,长度更大的数的数值也一定更大,一定也大于 231−12^{31}-1231−1,因此不可能继续拆分得到斐波那契式序列;
如果列表中至少有 222 个数,并且拆分出的数已经大于最后 222 个数的和,就不需要继续尝试拆分了。
当整个字符串拆分完毕时,如果列表中至少有 333 个数,则得到一个符合要求的斐波那契式序列,返回列表。如果没有找到符合要求的斐波那契式序列,则返回空列表。
实现方面,回溯需要带返回值,表示是否存在符合要求的斐波那契式序列。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/jiang-shu-zu-chai-fen-cheng-fei-bo-na-qi-ts6c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 class Solution: 2 def splitIntoFibonacci(self, S: str) -> List[int]: 3 ans = list() 4 5 def backtrack(index: int): 6 if index == len(S): 7 return len(ans) >= 3 8 9 curr = 0 10 for i in range(index, len(S)): 11 if i > index and S[index] == "0": 12 break 13 curr = curr * 10 + ord(S[i]) - ord("0") 14 if curr > 2**31 - 1: 15 break 16 17 if len(ans) < 2 or curr == ans[-2] + ans[-1]: 18 ans.append(curr) 19 if backtrack(i + 1): 20 return True 21 ans.pop() 22 elif len(ans) > 2 and curr > ans[-2] + ans[-1]: 23 break 24 25 return False 26 27 backtrack(0) 28 return ans 29 30 31 #作者:LeetCode-Solution 32 #链接:https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/jiang-shu-zu-chai-fen-cheng-fei-bo-na-qi-ts6c/ 33 #来源:力扣(LeetCode) 34 #著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 class Solution { 2 public: 3 vector<int> ans; 4 bool check(){ 5 if(ans.size() < 3) return true; 6 return (long long)ans.back() == (long long)ans[ans.size() - 2] + (long long)ans[ans.size() - 3]; 7 } 8 bool dfs(string& s, int ind){ 9 if(ind >= s.size()) return ans.size() > 2; // 出口,ans.size()大于2表示找到一个合法的答案了 10 if(s[ind] == '0'){ 11 ans.push_back(0); 12 if(check() && dfs(s, ind + 1)) return true; // 本次选择合法并且后续搜索也找到答案 13 ans.pop_back(); 14 return false; // 回溯 15 } 16 long long cur = 0; // 枚举所有不超过INT_MAX的数作为本层选择 17 while(ind < s.size()){ 18 cur = cur * 10 + s[ind++] - '0'; 19 if(cur > INT_MAX) return false; // 枚举已经超过INT_MAX却还没找打答案,搜索失败 20 ans.push_back(cur); 21 if(check() && dfs(s, ind)) return true; // 本次选择合法并且后续搜索也找到答案 22 ans.pop_back(); 23 } 24 return false; // 回溯 25 } 26 vector<int> splitIntoFibonacci(string s) { 27 if(dfs(s, 0)) return ans; 28 return {}; 29 } 30 }; 31 32 33 //作者:Monologue-S 34 //链接:https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/cshen-sou-jian-dan-30xing-4ms-by-monolog-y0bz/ 35 //来源:力扣(LeetCode) 36 //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度:O(nlog2C)O(n log^2 C)O(nlog2C),其中 nnn 是字符串的长度,CCC 是题目规定的整数范围 231−12^{31}-1231−1。在回溯的过程中,实际上真正进行「回溯」的只有前 222 个数,而从第 333 个数开始,整个斐波那契数列是可以被唯一确定的,整个回溯过程只起到验证(而不是枚举)的作用。对于前 222 个数,它们的位数不能超过 ⌊log10C⌋lfloor log_{10} C
floor⌊log10C⌋,那么枚举的空间为 O(log2C)O(log^2 C)O(log2C);对于后面的所有数,回溯的过程是没有「分支」的,因此时间复杂度为 O(n)O(n)O(n),相乘即可得到总时间复杂度 O(nlog2C)O(n log^2 C)O(nlog2C)。
空间复杂度:O(n)O(n)O(n),其中 nnn 是字符串的长度。除了返回值以外,空间复杂度主要取决于回溯过程中的递归调用层数,最大为 nnn。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/jiang-shu-zu-chai-fen-cheng-fei-bo-na-qi-ts6c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。