← 返回题目列表

拆分成斐波那契序列如何用回溯?为什么要做 32 位整数剪枝?

中等 第 22 / 30 题 更新于 2026/07/31
回溯字符串斐波那契

简化版

把字符串拆成斐波那契序列,可以用回溯枚举切分。

前两个数可以自由选择;从第三个数开始,当前数必须等于前两个数之和。

如果数字有前导零、超过 32 位整数范围,或者已经大于期望和,就可以剪枝。

详细版

递归状态是当前位置 index 和当前序列 path

每次从 index 开始尝试切出一个数字 cur。如果路径长度小于 2,可以尝试加入;否则计算 need = path[-1] + path[-2],当前数字必须等于 need

题目通常要求每个数在 32 位有符号整数范围内,即不超过 2^31 - 1

index 到末尾且路径长度至少为 3,返回这条序列。

完整版教学

一、这题和累加数有什么关系

拆分斐波那契序列和累加数非常像,都要求从第三个数开始等于前两个数之和。区别是这题要返回具体序列,并且常见题目要求每个数字不超过 2^31 - 1。所以它既是字符串切分问题,也是带数值范围的回溯问题。

记忆钩子:斐波那契拆分 = 累加数验证 + 返回路径 + 32 位范围。

二、前两个数为什么决定后面

一旦前两个数确定,第三个数必须是它们的和,第四个数也被继续确定。因此搜索自由度主要集中在前两个切分长度。后续每一步只是在验证字符串接下来是否匹配期望值。

阶段选择空间
第 1 个数多种切法
第 2 个数多种切法
第 3 个数以后必须等于前两数之和

这就是剪枝强的原因。

三、前导零和整数范围

如果片段以 0 开头,长度大于 1,就非法。例如 "01" 不能当成 1。另外每个数不能超过 2147483647。当当前数字已经超过上限,就可以停止当前层枚举,因为继续延长只会更大。

MAX = 2^31 - 1 = 2147483647
cur > MAX -> break

这类剪枝能避免无意义的大数分支。

四、代码模板

实现如下:

function splitIntoFibonacci(num) {
  const path = []
  const MAX = 2147483647

  function dfs(index) {
    if (index === num.length) return path.length >= 3
    let cur = 0
    for (let end = index; end < num.length; end++) {
      if (num[index] === '0' && end > index) break
      cur = cur * 10 + Number(num[end])
      if (cur > MAX) break
      const len = path.length
      if (len >= 2) {
        const need = path[len - 1] + path[len - 2]
        if (cur < need) continue
        if (cur > need) break
      }
      path.push(cur)
      if (dfs(end + 1)) return true
      path.pop()
    }
    return false
  }

  return dfs(0) ? path : []
}

这里直接用增量构造 cur,避免频繁 slice 再转数字。

五、带数字推演

字符串 "123456579" 可以拆成:

123, 456, 579

因为 123 + 456 = 579。如果前两个切成 1,2,第三个需要 3,后续需要 5,但字符串后面是 4,这个分支会失败。

六、常见误区与追问

  • 误区:找到前 3 个满足就结束。 必须覆盖完整字符串。
  • 误区:忽略 32 位上限。 超范围数字不符合题目要求。
  • 误区:允许前导零。 "01" 这种片段非法,只能单独使用 "0"
  • 追问:为什么 cur > need 可以 break? 继续加位只会让 cur 更大,不可能再匹配 need。
  • 追问:如果有多个答案怎么办? 常见题返回任意一个合法序列即可,找到后可以立刻返回。

这些点考的是回溯剪枝的边界。

七、加强记忆

拆分斐波那契记成“先猜两段,后面按和验证”。每段要过前导零和 32 位上限;从第三段开始,当前值小于期望就继续加位,大于期望就停止当前分支,等于才递归。走完整个字符串且长度至少 3,才返回路径。