数字拆分问题算法回溯_斐波那契的拆分—回溯算法经典例题

斐波那契的拆分—回溯算法经典例题

给定一个数字字符串 s,将它拆分成一个长度大于 3 的斐波那契式的列表。也就是说,除第一个和第二个数字,列表中的每个数字都等于前两个数字的和。

例如:

s="1011112",输出:[10,1,11,12]。

s = "12345168",输出:[123,45,168]。

s = "1234",输出:[]

解题思路如下:

1) 我们先假设第一个数字是 s 字符串中的第一个数字,比如 "12345168910" 中的 1。然后再假设第二个数字是 s 字符串中的第二个数字,比如 "12345168910" 中的 2。

2) 接下来检查这个假设是否成立。假设不成立,因为 1+2=3,但 2+3≠4。所以我们退回到第二个数字,假设第二个数字是 s 字符串中的第二个数字和第三个数字,比如"12345168910"中的23,然后再次检查这个假设是否成立。通过判断,这个假设仍然不成立。

3) 事实上,尝试过后,我们发现第二个数字为 2,23,234,2345,23451,234516,2345168,23451689, 234516891,2345168910 这些假设都不成立。这时我们只好退回到第一个数字,使它变为字符串中的第一个和第二个数字,比如 "12345168910"中的 12。而第二个数字则是 s 字符串的下一个的数字—— 3。我们重复这个步骤直到找到成立的两个数字,或者将所有可能都尝试完得出没有解的结论。

4) 在以上过程中,假设第三个数字成立了,也就是说目前找到了一个数字是前两个数字的和。接下来,我们要检查第四个数字是否成立。我们重复这个步骤,一旦数字不成立,就退回到第二个数字,改变它,然后再继续。

比如,我们找到了 168=123+45。现在,我们需要验证第四个数字。因为后三位数字不是我们想要的 291,所以我们退回到第二个数字,改变它。尝试完所有的第二个数字的可能并失败后,我们会返回到第一个数字,改变它,然后再继续。

以上就是解题思路,但是我们还可以通过剪枝来提高解题的效率。

本题有两处可以提升效率的部分。

1)  在判断两个数字的和是否在剩余的字符串中时,如果两个数字的和小于当前数字,就可以直接得到否定的结论。比如当 s="42834",第一个数字是 4,第二个数字是 2 时,我们检查 4+2 是否等于 8。4+2 不但不等于 8,还小于 8,这意味着我们不需要再检查 4+2 是否等于 83。但是,如果 s="93129",第一个数字是 9,第二个数字是 3,那么我们除检查 9+3 是否等于 1,还需要检查 9+3 是否等于 12。

2) 像“01”“004”这样的数字不成立,可以直接跳过它们。

完整代码如下:

#将s拆分成一个长度大于3的斐波那契式的列表

def splitIntoFibonacci(s):

#res是当前结果列表,index是当前数字的开始坐标

#以res[-1], res[-2]为前两位数字,检查第三位数字是否成立

def helper(s,res, index):

if index ==n and len(res)>=3: #如果所有数字都被遍历并且res有3个以上数字

return True

for i in range(index,n):

if s[index] == "0" and i>index: #不成立的数字

break

num = int(s[index:i+1]) #动态变化的当前数字

#第三个数字不成立

if len(res)>=2 and num > res[-1] + res[-2]:

break

#第三个数字暂时不存在或第三个数字成立

if len(res)<=1 or num == res [-1] + res[-2]:

res.append(num)

if helper(s, res,i+1): #检查第四个数字

return True

res.pop()

return False

res = []

n = len(s)

helper(s,res,0)

return res

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值