A clear explanation of Additive Number using split enumeration and deterministic checking.
Problem Restatement
We are given a string num containing only digits.
Return true if the digits can be split into an additive sequence. Otherwise, return false.
An additive sequence must satisfy three rules:
| Rule | Meaning |
|---|---|
| At least three numbers | We need a, b, c, not only a, b |
| Sum rule | Every number after the first two equals the sum of the previous two |
| No leading zeros | Numbers like "03" and "012" are invalid |
For example, "112358" is valid because it can be split as:
1, 1, 2, 3, 5, 8and:
1 + 1 = 2
1 + 2 = 3
2 + 3 = 5
3 + 5 = 8The official statement says the input length is at most 35, and the follow-up asks how to handle overflow for very large integers. Python integers already handle arbitrary precision.
Input and Output
| Item | Meaning |
|---|---|
| Input | A digit string num |
| Output | true if num can form an additive sequence, otherwise false |
| Constraint | 1 <= num.length <= 35 |
| Digits only | num contains only characters '0' through '9' |
Function shape:
def isAdditiveNumber(num: str) -> bool:
...Examples
Example 1:
num = "112358"Output:
TrueValid split:
1, 1, 2, 3, 5, 8Example 2:
num = "199100199"Output:
TrueValid split:
1, 99, 100, 199because:
1 + 99 = 100
99 + 100 = 199Example 3:
num = "1023"Output:
FalseOne tempting split is:
1, 02, 3But "02" has a leading zero, so it is invalid.
First Thought: Try Every Split
We could try to split the string into every possible list of numbers.
For each split, check whether the sequence is additive.
But this creates too many possibilities because every gap between two digits can either be a cut or not a cut.
For a string of length n, there are many possible partitions.
We need a smaller search.
Key Insight
Once we choose the first two numbers, the rest of the sequence is forced.
If the first two numbers are:
a = 1
b = 99then the next number must be:
a + b = 100After that, the next number must be:
99 + 100 = 199So we only need to enumerate the first two numbers.
After choosing them, we scan the rest of the string deterministically.
Handling Leading Zeros
A number can be exactly "0".
But a multi-digit number cannot start with "0".
Valid:
0Invalid:
01
00
012So when choosing the first number or second number, we stop if that substring has a leading zero.
The same rule is automatically handled for later numbers because later numbers are generated by addition and converted to normal decimal strings.
Algorithm
Let n = len(num).
Try every possible end position for the first number.
Then try every possible end position for the second number.
For each pair:
- Reject the first number if it has a leading zero.
- Reject the second number if it has a leading zero.
- Convert both numbers to integers.
- Starting after the second number, repeatedly:
- Compute
next_value = first + second. - Convert it to a string.
- Check whether the remaining input starts with that string.
- If not, this split fails.
- Move forward and update the pair.
- Compute
- If we consume the whole string and used at least three numbers, return
True.
If no split works, return False.
Correctness
The algorithm checks every valid choice of the first number and second number.
For any additive sequence, the first two numbers uniquely determine every later number because each later number must be the sum of the previous two.
So when the algorithm tests the correct first two numbers, the deterministic checking step must follow exactly the same sequence. Each generated sum must match the next digits of num.
If the algorithm consumes the whole string, then the string has been split into at least three numbers, and every number after the first two equals the sum of the previous two. The leading-zero checks guarantee that all numbers have valid decimal representation.
If the algorithm returns False, then every possible first-two-number split failed. Since every additive sequence must have some first two numbers, no valid additive sequence exists.
Therefore, the algorithm is correct.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n^3) | There are O(n^2) first-two splits, and each verification can scan up to O(n) characters |
| Space | O(n) | String forms of generated sums can have length up to n |
For n <= 35, this is easily fast enough.
Implementation
class Solution:
def isAdditiveNumber(self, num: str) -> bool:
n = len(num)
def has_leading_zero(s: str) -> bool:
return len(s) > 1 and s[0] == "0"
def valid_from(first: int, second: int, start: int) -> bool:
count = 2
while start < n:
third = first + second
third_str = str(third)
if not num.startswith(third_str, start):
return False
start += len(third_str)
first = second
second = third
count += 1
return count >= 3
for i in range(1, n):
first_str = num[:i]
if has_leading_zero(first_str):
break
for j in range(i + 1, n):
second_str = num[i:j]
if has_leading_zero(second_str):
break
first = int(first_str)
second = int(second_str)
if valid_from(first, second, j):
return True
return FalseCode Explanation
We store the length:
n = len(num)The helper for leading zeros is:
def has_leading_zero(s: str) -> bool:
return len(s) > 1 and s[0] == "0"This allows "0" but rejects "01".
The main verifier starts with two chosen numbers and a string index.
def valid_from(first: int, second: int, start: int) -> bool:At each step, the next number is forced:
third = first + second
third_str = str(third)Then we check whether the remaining string starts with that sum.
if not num.startswith(third_str, start):
return FalseIf it matches, move the index forward.
start += len(third_str)Then shift the last two numbers:
first = second
second = thirdIf we consume the whole string, the split is valid.
return count >= 3Now the outer loops choose the first two numbers.
for i in range(1, n):
first_str = num[:i]i is the end of the first number.
If the first number has a leading zero, longer first numbers will also have a leading zero, so we stop.
if has_leading_zero(first_str):
breakThen choose the second number.
for j in range(i + 1, n):
second_str = num[i:j]If the second number has a leading zero, longer second numbers starting at the same place will also be invalid, so we stop that inner loop.
if has_leading_zero(second_str):
breakIf the deterministic check succeeds, return True.
if valid_from(first, second, j):
return TrueIf every split fails, return False.
Testing
def run_tests():
s = Solution()
assert s.isAdditiveNumber("112358") is True
assert s.isAdditiveNumber("199100199") is True
assert s.isAdditiveNumber("1023") is False
assert s.isAdditiveNumber("000") is True
assert s.isAdditiveNumber("101") is True
assert s.isAdditiveNumber("1203") is False
assert s.isAdditiveNumber("12122436") is True
assert s.isAdditiveNumber("123") is True
assert s.isAdditiveNumber("12") is False
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"112358" | Classic additive sequence |
"199100199" | Multi-digit numbers |
"1023" | Leading-zero rejection |
"000" | Valid sequence 0, 0, 0 |
"101" | Valid sequence 1, 0, 1 |
"1203" | Rejects invalid continuation |
"12122436" | Valid sequence 12, 12, 24, 36 |
"123" | Minimum valid additive length |
"12" | Too short to form three numbers |