← 返回题目列表

累加数如何用回溯验证?前导零和大整数怎么处理?

中等 第 29 / 30 题 更新于 2026/07/31
回溯字符串大整数

简化版

累加数要求字符串能拆成至少 3 个数,并且从第三个数开始,每个数等于前两个数之和。

回溯时依次切出数字,路径里已有至少两个数时,新数字必须等于前两个数之和。

如果某段数字以 0 开头且长度大于 1,要直接跳过。

详细版

递归状态是当前切分下标 index 和已经切出的数字路径 path

index 开始尝试不同长度的数字片段。每个片段要检查:

  • 前导零是否合法;
  • 如果已有两个数字,当前数字是否等于前两个之和;
  • 如果当前数字已经大于期望和,可以提前停止。

JavaScript 中数字可能超过安全整数,稳妥写法可以用 BigInt

当走到字符串末尾且路径长度至少为 3,说明是累加数。

完整版教学

一、累加数的结构是什么

累加数像字符串版斐波那契:前两个数自由选择,后面的数被前两个数唯一决定。比如 "112358" 可以拆成 1,1,2,3,5,8。这类题的搜索重点在前两个数怎么切,以及后续是否严格匹配。

记忆钩子:累加数只有前两个数有自由度,后面都被和约束住。

二、回溯如何切字符串

从当前位置 index 出发,枚举结束位置 end,得到片段 s[index..end]。把片段转成数字后,判断它能否加入当前路径。如果可以,就递归处理后面的字符串。

路径长度当前片段要求
0任意合法数字
1任意合法数字
>=2必须等于前两个数之和

这个约束让搜索树很快收缩。

三、前导零为什么要剪掉

数字不能有前导零,除非它本身就是 "0"。例如 "01" 不能当成数字 1。否则同一个数字会有多种字符串表示,题目语义也会混乱。

"0" 合法
"01" 不合法
"001" 不合法

所以一旦 s[index] === '0',当前层只能尝试长度为 1 的片段。

四、代码模板

实现如下:

function isAdditiveNumber(num) {
  const path = []

  function dfs(index) {
    if (index === num.length) return path.length >= 3
    for (let end = index; end < num.length; end++) {
      if (num[index] === '0' && end > index) break
      const cur = BigInt(num.slice(index, end + 1))
      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)
}

BigInt 可以避免超出 Number 安全整数范围。

五、带数字推演

字符串 "112358"

先切 1, 1
下一个必须是 2,匹配
下一个必须是 3,匹配
下一个必须是 5,匹配
下一个必须是 8,匹配
到末尾且长度 >= 3,返回 true

如果切成 11,2,下一个必须是 13,但后面开头是 3,会失败。

六、常见误区与追问

  • 误区:允许 01 这种数字片段。 前导零会产生非法切分。
  • 误区:只要出现三个数满足一次相加就返回 true。 必须整个字符串都能按规则切完。
  • 误区:用 Number 处理特别长字符串。 可能超过安全整数,比较结果不可靠。
  • 追问:为什么 cur > need 可以 break? 当前片段继续变长只会更大,不可能重新等于 need。
  • 追问:复杂度高吗? 前两个数有多种切法,但后续被和约束,实际剪枝较强。

这些问题考的是字符串切分和数值约束。

七、加强记忆

累加数记成“前两个随便切,后面必须等于和”。每段先过前导零检查;路径长度达到 2 后,当前数只能匹配前两个数之和。用 BigInt 防止大整数失真。走到末尾且至少 3 个数,才算验证成功。