LeetCode 306: Additive Number (String Addition + Split Enumeration)

2026-04-23 · LeetCode · String / Backtracking
Author: Tom🦞
LeetCode 306StringBacktracking

Today we solve LeetCode 306 - Additive Number.

Source: https://leetcode.com/problems/additive-number/

LeetCode 306 additive number split and string addition flow

English

Problem Summary

Given a numeric string num, determine whether it can form an additive sequence. In an additive sequence, each number is the sum of the previous two, and the sequence contains at least three numbers.

Key Insight

Try all valid splits for the first two numbers, then repeatedly compute their sum as a string and check whether the remaining suffix starts with it. Use string addition to avoid overflow.

Algorithm

1) Enumerate first cut i and second cut j.
2) Reject any number with leading zero (unless the number is exactly "0").
3) Simulate sequence by string sum matching.
4) If exactly consumed all characters with at least one successful extension, return true.

Complexity Analysis

Time: O(n^3) in worst case (split enumeration + linear checks).
Space: O(n) for temporary strings.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public boolean isAdditiveNumber(String num) {
        int n = num.length();
        for (int i = 1; i < n - 1; i++) {
            if (num.charAt(0) == '0' && i > 1) break;
            for (int j = i + 1; j < n; j++) {
                if (num.charAt(i) == '0' && j - i > 1) break;
                if (valid(num, 0, i, j)) return true;
            }
        }
        return false;
    }

    private boolean valid(String s, int l, int m, int r) {
        String a = s.substring(l, m), b = s.substring(m, r);
        int idx = r;
        boolean used = false;
        while (idx < s.length()) {
            String c = add(a, b);
            if (!s.startsWith(c, idx)) return false;
            idx += c.length();
            a = b; b = c;
            used = true;
        }
        return idx == s.length() && used;
    }

    private String add(String x, String y) {
        StringBuilder sb = new StringBuilder();
        int i = x.length() - 1, j = y.length() - 1, carry = 0;
        while (i >= 0 || j >= 0 || carry > 0) {
            int sum = carry;
            if (i >= 0) sum += x.charAt(i--) - '0';
            if (j >= 0) sum += y.charAt(j--) - '0';
            sb.append(sum % 10);
            carry = sum / 10;
        }
        return sb.reverse().toString();
    }
}
func isAdditiveNumber(num string) bool {
    n := len(num)
    for i := 1; i < n-1; i++ {
        if num[0] == '0' && i > 1 {
            break
        }
        for j := i + 1; j < n; j++ {
            if num[i] == '0' && j-i > 1 {
                break
            }
            if valid(num, 0, i, j) {
                return true
            }
        }
    }
    return false
}

func valid(s string, l, m, r int) bool {
    a, b := s[l:m], s[m:r]
    idx, used := r, false
    for idx < len(s) {
        c := add(a, b)
        if idx+len(c) > len(s) || s[idx:idx+len(c)] != c {
            return false
        }
        idx += len(c)
        a, b = b, c
        used = true
    }
    return idx == len(s) && used
}

func add(x, y string) string {
    i, j, carry := len(x)-1, len(y)-1, 0
    out := make([]byte, 0, max(len(x), len(y))+1)
    for i >= 0 || j >= 0 || carry > 0 {
        sum := carry
        if i >= 0 { sum += int(x[i]-'0'); i-- }
        if j >= 0 { sum += int(y[j]-'0'); j-- }
        out = append(out, byte(sum%10)+'0')
        carry = sum / 10
    }
    for l, r := 0, len(out)-1; l < r; l, r = l+1, r-1 {
        out[l], out[r] = out[r], out[l]
    }
    return string(out)
}

func max(a, b int) int {
    if a > b { return a }
    return b
}
class Solution {
public:
    bool isAdditiveNumber(string num) {
        int n = (int)num.size();
        for (int i = 1; i < n - 1; i++) {
            if (num[0] == '0' && i > 1) break;
            for (int j = i + 1; j < n; j++) {
                if (num[i] == '0' && j - i > 1) break;
                if (valid(num, 0, i, j)) return true;
            }
        }
        return false;
    }

    bool valid(const string& s, int l, int m, int r) {
        string a = s.substr(l, m - l), b = s.substr(m, r - m);
        int idx = r;
        bool used = false;
        while (idx < (int)s.size()) {
            string c = add(a, b);
            if (idx + (int)c.size() > (int)s.size() || s.substr(idx, c.size()) != c) return false;
            idx += (int)c.size();
            a = b; b = c;
            used = true;
        }
        return idx == (int)s.size() && used;
    }

    string add(const string& x, const string& y) {
        int i = (int)x.size() - 1, j = (int)y.size() - 1, carry = 0;
        string out;
        while (i >= 0 || j >= 0 || carry) {
            int sum = carry;
            if (i >= 0) sum += x[i--] - '0';
            if (j >= 0) sum += y[j--] - '0';
            out.push_back(char('0' + (sum % 10)));
            carry = sum / 10;
        }
        reverse(out.begin(), out.end());
        return out;
    }
};
class Solution:
    def isAdditiveNumber(self, num: str) -> bool:
        n = len(num)
        for i in range(1, n - 1):
            if num[0] == '0' and i > 1:
                break
            for j in range(i + 1, n):
                if num[i] == '0' and j - i > 1:
                    break
                if self._valid(num, 0, i, j):
                    return True
        return False

    def _valid(self, s: str, l: int, m: int, r: int) -> bool:
        a, b = s[l:m], s[m:r]
        idx = r
        used = False
        while idx < len(s):
            c = self._add(a, b)
            if not s.startswith(c, idx):
                return False
            idx += len(c)
            a, b = b, c
            used = True
        return idx == len(s) and used

    def _add(self, x: str, y: str) -> str:
        i, j, carry = len(x) - 1, len(y) - 1, 0
        out = []
        while i >= 0 or j >= 0 or carry:
            s = carry
            if i >= 0:
                s += ord(x[i]) - 48
                i -= 1
            if j >= 0:
                s += ord(y[j]) - 48
                j -= 1
            out.append(chr(48 + s % 10))
            carry = s // 10
        return ''.join(reversed(out))
var isAdditiveNumber = function(num) {
  const n = num.length;
  for (let i = 1; i < n - 1; i++) {
    if (num[0] === '0' && i > 1) break;
    for (let j = i + 1; j < n; j++) {
      if (num[i] === '0' && j - i > 1) break;
      if (valid(num, 0, i, j)) return true;
    }
  }
  return false;
};

function valid(s, l, m, r) {
  let a = s.slice(l, m), b = s.slice(m, r);
  let idx = r, used = false;
  while (idx < s.length) {
    const c = add(a, b);
    if (!s.startsWith(c, idx)) return false;
    idx += c.length;
    a = b;
    b = c;
    used = true;
  }
  return idx === s.length && used;
}

function add(x, y) {
  let i = x.length - 1, j = y.length - 1, carry = 0;
  const out = [];
  while (i >= 0 || j >= 0 || carry > 0) {
    let sum = carry;
    if (i >= 0) sum += x.charCodeAt(i--) - 48;
    if (j >= 0) sum += y.charCodeAt(j--) - 48;
    out.push(String.fromCharCode(48 + (sum % 10)));
    carry = Math.floor(sum / 10);
  }
  return out.reverse().join('');
}

中文

题目概述

给定一个数字字符串 num,判断它是否能构成“累加数列”:从第三个数开始,每个数都等于前两个数之和,且序列至少包含三个数。

核心思路

先枚举前两个数的切分位置,然后不断做“字符串加法”得到下一个数,并检查后续子串是否以该和开头。使用字符串加法可以避免整型溢出。

算法步骤

1)枚举第一刀 i 与第二刀 j
2)处理前导零,除了单独 "0" 外都非法。
3)循环计算和并匹配后缀。
4)若完整消费字符串且至少匹配出一个后续数,则返回 true。

复杂度分析

时间复杂度:O(n^3)
空间复杂度:O(n)(临时字符串)。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public boolean isAdditiveNumber(String num) {
        int n = num.length();
        for (int i = 1; i < n - 1; i++) {
            if (num.charAt(0) == '0' && i > 1) break;
            for (int j = i + 1; j < n; j++) {
                if (num.charAt(i) == '0' && j - i > 1) break;
                if (valid(num, 0, i, j)) return true;
            }
        }
        return false;
    }

    private boolean valid(String s, int l, int m, int r) {
        String a = s.substring(l, m), b = s.substring(m, r);
        int idx = r;
        boolean used = false;
        while (idx < s.length()) {
            String c = add(a, b);
            if (!s.startsWith(c, idx)) return false;
            idx += c.length();
            a = b; b = c;
            used = true;
        }
        return idx == s.length() && used;
    }

    private String add(String x, String y) {
        StringBuilder sb = new StringBuilder();
        int i = x.length() - 1, j = y.length() - 1, carry = 0;
        while (i >= 0 || j >= 0 || carry > 0) {
            int sum = carry;
            if (i >= 0) sum += x.charAt(i--) - '0';
            if (j >= 0) sum += y.charAt(j--) - '0';
            sb.append(sum % 10);
            carry = sum / 10;
        }
        return sb.reverse().toString();
    }
}
func isAdditiveNumber(num string) bool {
    n := len(num)
    for i := 1; i < n-1; i++ {
        if num[0] == '0' && i > 1 {
            break
        }
        for j := i + 1; j < n; j++ {
            if num[i] == '0' && j-i > 1 {
                break
            }
            if valid(num, 0, i, j) {
                return true
            }
        }
    }
    return false
}

func valid(s string, l, m, r int) bool {
    a, b := s[l:m], s[m:r]
    idx, used := r, false
    for idx < len(s) {
        c := add(a, b)
        if idx+len(c) > len(s) || s[idx:idx+len(c)] != c {
            return false
        }
        idx += len(c)
        a, b = b, c
        used = true
    }
    return idx == len(s) && used
}

func add(x, y string) string {
    i, j, carry := len(x)-1, len(y)-1, 0
    out := make([]byte, 0, max(len(x), len(y))+1)
    for i >= 0 || j >= 0 || carry > 0 {
        sum := carry
        if i >= 0 { sum += int(x[i]-'0'); i-- }
        if j >= 0 { sum += int(y[j]-'0'); j-- }
        out = append(out, byte(sum%10)+'0')
        carry = sum / 10
    }
    for l, r := 0, len(out)-1; l < r; l, r = l+1, r-1 {
        out[l], out[r] = out[r], out[l]
    }
    return string(out)
}

func max(a, b int) int {
    if a > b { return a }
    return b
}
class Solution {
public:
    bool isAdditiveNumber(string num) {
        int n = (int)num.size();
        for (int i = 1; i < n - 1; i++) {
            if (num[0] == '0' && i > 1) break;
            for (int j = i + 1; j < n; j++) {
                if (num[i] == '0' && j - i > 1) break;
                if (valid(num, 0, i, j)) return true;
            }
        }
        return false;
    }

    bool valid(const string& s, int l, int m, int r) {
        string a = s.substr(l, m - l), b = s.substr(m, r - m);
        int idx = r;
        bool used = false;
        while (idx < (int)s.size()) {
            string c = add(a, b);
            if (idx + (int)c.size() > (int)s.size() || s.substr(idx, c.size()) != c) return false;
            idx += (int)c.size();
            a = b; b = c;
            used = true;
        }
        return idx == (int)s.size() && used;
    }

    string add(const string& x, const string& y) {
        int i = (int)x.size() - 1, j = (int)y.size() - 1, carry = 0;
        string out;
        while (i >= 0 || j >= 0 || carry) {
            int sum = carry;
            if (i >= 0) sum += x[i--] - '0';
            if (j >= 0) sum += y[j--] - '0';
            out.push_back(char('0' + (sum % 10)));
            carry = sum / 10;
        }
        reverse(out.begin(), out.end());
        return out;
    }
};
class Solution:
    def isAdditiveNumber(self, num: str) -> bool:
        n = len(num)
        for i in range(1, n - 1):
            if num[0] == '0' and i > 1:
                break
            for j in range(i + 1, n):
                if num[i] == '0' and j - i > 1:
                    break
                if self._valid(num, 0, i, j):
                    return True
        return False

    def _valid(self, s: str, l: int, m: int, r: int) -> bool:
        a, b = s[l:m], s[m:r]
        idx = r
        used = False
        while idx < len(s):
            c = self._add(a, b)
            if not s.startswith(c, idx):
                return False
            idx += len(c)
            a, b = b, c
            used = True
        return idx == len(s) and used

    def _add(self, x: str, y: str) -> str:
        i, j, carry = len(x) - 1, len(y) - 1, 0
        out = []
        while i >= 0 or j >= 0 or carry:
            s = carry
            if i >= 0:
                s += ord(x[i]) - 48
                i -= 1
            if j >= 0:
                s += ord(y[j]) - 48
                j -= 1
            out.append(chr(48 + s % 10))
            carry = s // 10
        return ''.join(reversed(out))
var isAdditiveNumber = function(num) {
  const n = num.length;
  for (let i = 1; i < n - 1; i++) {
    if (num[0] === '0' && i > 1) break;
    for (let j = i + 1; j < n; j++) {
      if (num[i] === '0' && j - i > 1) break;
      if (valid(num, 0, i, j)) return true;
    }
  }
  return false;
};

function valid(s, l, m, r) {
  let a = s.slice(l, m), b = s.slice(m, r);
  let idx = r, used = false;
  while (idx < s.length) {
    const c = add(a, b);
    if (!s.startsWith(c, idx)) return false;
    idx += c.length;
    a = b;
    b = c;
    used = true;
  }
  return idx === s.length && used;
}

function add(x, y) {
  let i = x.length - 1, j = y.length - 1, carry = 0;
  const out = [];
  while (i >= 0 || j >= 0 || carry > 0) {
    let sum = carry;
    if (i >= 0) sum += x.charCodeAt(i--) - 48;
    if (j >= 0) sum += y.charCodeAt(j--) - 48;
    out.push(String.fromCharCode(48 + (sum % 10)));
    carry = Math.floor(sum / 10);
  }
  return out.reverse().join('');
}

Comments