A clear explanation of the Split Array into Fibonacci Sequence problem using backtracking, leading-zero checks, and 32-bit integer limits.
Problem Restatement
We are given a string num containing only digits.
We need to split it into a Fibonacci-like sequence.
A valid Fibonacci-like sequence must satisfy these rules:
| Rule | Meaning |
|---|---|
| Length | The sequence must contain at least 3 numbers |
| Value limit | Every number must fit in a 32-bit signed integer |
| Fibonacci rule | seq[i] + seq[i + 1] == seq[i + 2] |
| Leading zero rule | A number cannot have extra leading zeroes |
Return any valid sequence.
If no valid sequence exists, return an empty list. The official statement gives examples such as "123456579" -> [123, 456, 579], and notes that every value must be between 0 and 2^31 - 1.
Input and Output
| Item | Meaning |
|---|---|
| Input | A digit string num |
| Output | A list of integers forming a Fibonacci-like sequence |
| Failure case | Return [] |
| Minimum sequence length | 3 |
| Maximum integer | 2^31 - 1 |
Function shape:
class Solution:
def splitIntoFibonacci(self, num: str) -> list[int]:
...Examples
Example 1:
num = "123456579"We can split it as:
[123, 456, 579]because:
123 + 456 = 579So the answer can be:
[123, 456, 579]Example 2:
num = "11235813"We can split it as:
[1, 1, 2, 3, 5, 8, 13]Each number is the sum of the previous two.
Example 3:
num = "112358130"There is no valid split.
So the answer is:
[]Example 4:
num = "0123"We cannot use "01" as a number because leading zeroes are not allowed.
So the answer is:
[]First Thought: Try Every Split
The string can be cut into many possible numbers.
For example:
"123456579"could start as:
1, ...
12, ...
123, ...
1234, ...Once we pick the first two numbers, the rest of the sequence is almost fixed. The next number must be their sum.
So a natural approach is backtracking:
- Try a number starting at the current index.
- Check whether it is valid.
- If the sequence has at least two numbers, check whether the new number equals the sum of the previous two.
- Continue recursively.
- Backtrack if the path fails.
Key Insight
At any point, if the current sequence already has two numbers, the next number has only one possible value:
seq[-2] + seq[-1]So we can prune early.
When building a candidate number from the string:
| Invalid case | Action |
|---|---|
| It has a leading zero | Stop extending this number |
It exceeds 2^31 - 1 | Stop extending this number |
| It is greater than expected Fibonacci sum | Stop this branch |
| It is less than expected Fibonacci sum | Try extending with one more digit |
This makes the search much smaller than trying all partitions blindly.
Algorithm
Use DFS with a path list seq.
At index start:
If
start == len(num):- Return
Trueiflen(seq) >= 3. - Otherwise return
False.
- Return
Build the next number by trying each end index from
startto the end of the string.If
num[start] == "0"and the number has more than one digit, stop.Convert the substring to an integer.
If the value exceeds
2^31 - 1, stop.If
len(seq) >= 2:- Let
expected = seq[-2] + seq[-1]. - If
value < expected, continue. - If
value > expected, stop. - If equal, use it.
- Let
Add the value to
seq.Recurse from the next index.
If recursion succeeds, return
True.Otherwise remove the value and try another split.
If no branch works, return False.
Walkthrough
Use:
num = "11235813"Start with an empty sequence:
seq = []Choose first number:
1Now:
seq = [1]Choose second number:
1Now:
seq = [1, 1]The next expected number is:
1 + 1 = 2The next digit is "2", so add it:
seq = [1, 1, 2]Next expected:
1 + 2 = 3Add 3:
seq = [1, 1, 2, 3]Next expected:
2 + 3 = 5Add 5:
seq = [1, 1, 2, 3, 5]Next expected:
3 + 5 = 8Add 8:
seq = [1, 1, 2, 3, 5, 8]Next expected:
5 + 8 = 13The remaining string is "13", so add it:
seq = [1, 1, 2, 3, 5, 8, 13]Now all digits are used and the sequence length is at least 3.
Return this sequence.
Correctness
The DFS tries every possible valid substring starting at the current index, subject to the problem’s constraints.
It rejects numbers with extra leading zeroes, so every number it adds to seq has a valid decimal representation.
It rejects numbers larger than 2^31 - 1, so every number it adds satisfies the 32-bit signed integer limit.
When seq has fewer than two numbers, any valid number can be chosen because the Fibonacci rule has not started yet.
When seq has at least two numbers, the next number must equal seq[-2] + seq[-1]. The DFS only accepts the candidate number when it equals that sum. Therefore, every completed sequence returned by the algorithm satisfies the Fibonacci rule.
The DFS explores all possible choices for the first number, second number, and every later valid continuation. If a valid split exists, its first number appears among the tried prefixes, its second number appears among the tried prefixes after that, and every later number is accepted because it equals the required sum. So the DFS will find it.
If the DFS finishes without success, then every valid partition has been rejected for a real constraint violation or a Fibonacci mismatch. Therefore, no valid sequence exists.
Complexity
Let:
n = len(num)| Metric | Value | Why |
|---|---|---|
| Time | O(n^2) in practice | The main choices are the first two numbers; later values are forced |
| Space | O(n) | Recursion path can contain up to n one-digit numbers |
The 32-bit limit also bounds candidate number length to at most 10 digits, which keeps branching small.
Implementation
class Solution:
def splitIntoFibonacci(self, num: str) -> list[int]:
limit = 2**31 - 1
seq = []
def dfs(start: int) -> bool:
if start == len(num):
return len(seq) >= 3
value = 0
for end in range(start, len(num)):
if end > start and num[start] == "0":
break
value = value * 10 + int(num[end])
if value > limit:
break
if len(seq) >= 2:
expected = seq[-2] + seq[-1]
if value < expected:
continue
if value > expected:
break
seq.append(value)
if dfs(end + 1):
return True
seq.pop()
return False
dfs(0)
return seqCode Explanation
The maximum allowed value is:
limit = 2**31 - 1The current candidate sequence is stored in:
seq = []The DFS starts at an index in the string:
def dfs(start: int) -> bool:If all digits are used, the split is valid only if it has at least three numbers:
if start == len(num):
return len(seq) >= 3We build the next number digit by digit:
value = value * 10 + int(num[end])Leading zeroes are rejected:
if end > start and num[start] == "0":
breakFor example, "0" is allowed, but "01" is not.
The 32-bit limit is enforced here:
if value > limit:
breakIf there are already two numbers, the next value must match the Fibonacci sum:
expected = seq[-2] + seq[-1]If the current value is too small, we need more digits:
if value < expected:
continueIf it is too large, extending it will only make it larger, so this branch can stop:
if value > expected:
breakWhen the value is acceptable, add it:
seq.append(value)If the recursive call succeeds, return immediately:
if dfs(end + 1):
return TrueOtherwise, undo the choice:
seq.pop()Finally, the public function returns the found sequence, or [] if none was found.
Testing
def run_tests():
s = Solution()
assert s.splitIntoFibonacci("123456579") == [123, 456, 579]
assert s.splitIntoFibonacci("11235813") == [1, 1, 2, 3, 5, 8, 13]
assert s.splitIntoFibonacci("112358130") == []
assert s.splitIntoFibonacci("0123") == []
assert s.splitIntoFibonacci("1101111") in ([110, 1, 111], [11, 0, 11, 11])
assert s.splitIntoFibonacci("0000") == [0, 0, 0, 0]
assert s.splitIntoFibonacci("214748364721474836422147483641") == []
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"123456579" | Confirms multi-digit Fibonacci split |
"11235813" | Confirms classic Fibonacci sequence |
"112358130" | Confirms invalid suffix returns empty |
"0123" | Confirms leading zero rule |
"1101111" | Confirms multiple valid outputs may exist |
"0000" | Confirms zero is valid as a single digit |
| Large values | Confirms 32-bit integer limit |