3708. Longest Fibonacci Subarray
Problem Description
You are given an array of positive integers nums.
A Fibonacci array is a contiguous sequence in which every element from the third position onward equals the sum of the two elements immediately before it. In other words, for every valid index i starting from the third element, the condition nums[i] = nums[i-1] + nums[i-2] must hold.
Your task is to return the length of the longest Fibonacci subarray within nums.
Key points to understand:
- A subarray is a contiguous (unbroken) portion of the original array — you cannot skip or rearrange elements.
- Any subarray of length
1or2automatically counts as a Fibonacci array, since the "sum of the two preceding terms" rule only applies starting from the third element. This guarantees the answer is always at least2when the array has two or more elements. - The Fibonacci property here refers to the structure of the sequence (each term being the sum of the previous two), not to the classic Fibonacci numbers themselves. For example,
[4, 7, 11, 18]is a valid Fibonacci subarray because11 = 4 + 7and18 = 7 + 11.
Example walkthrough:
Suppose nums = [1, 1, 1, 1, 2, 3, 5, 1]:
- The subarray
[1, 1, 2, 3, 5]satisfies the rule:2 = 1 + 1,3 = 1 + 2,5 = 2 + 3. - Its length is
5, which would be the answer since no longer contiguous segment satisfies the condition.
The goal is simply to scan the array and find the longest stretch of consecutive elements where this "each term equals the sum of the previous two" relationship holds without interruption.
How We Pick the Algorithm
Why Simulation / Basic DSA?
This problem maps to Simulation / Basic DSA through a short path in the full flowchart.
Scanning for the longest Fibonacci-like consecutive streak is a simple single-pass run-length check with a rolling counter.
Open in FlowchartIntuition
The first thing to notice is that the Fibonacci condition is a local property: whether nums[i] extends a Fibonacci subarray depends only on its two immediate neighbors, nums[i-1] and nums[i-2]. We never need to look further back than two positions to make a decision.
This observation leads to a natural way of thinking about the problem: as we walk through the array from left to right, at each position we ask a simple yes/no question — does nums[i] == nums[i-1] + nums[i-2]?
- If yes, the current element seamlessly continues whatever Fibonacci streak was already running. The streak grows by exactly one.
- If no, the chain is broken at this point. But it does not reset to zero — since any two adjacent elements always form a valid Fibonacci subarray, the new streak ending at
nums[i]starts fresh with length2(the pairnums[i-1], nums[i]).
This is essentially a "longest run" pattern, similar to finding the longest streak of increasing elements: maintain a running counter f representing the length of the longest valid subarray ending at the current index, extend it when the condition holds, reset it when it fails, and track the maximum value seen along the way in ans.
A key insight is why a single break forces a full reset to 2 rather than some partial recovery: a Fibonacci subarray is fully determined by its first two elements. Once the condition fails at position i, no subarray containing both nums[i-2] and nums[i] through that point can ever be Fibonacci, so the only candidates going forward must begin at nums[i-1] or later. Starting the counter at 2 captures exactly that.
Because each element is examined once with constant work, this greedy single-pass scan runs in O(n) time and O(1) space — there is no need for nested loops or storing intermediate subarrays, since the local nature of the condition guarantees we never miss a longer answer.
Solution Approach
The implementation follows a dynamic programming pattern, simplified down to a single rolling variable since each state only depends on the previous one.
State definition:
Let f represent the length of the longest Fibonacci subarray ending at the current index. The final answer ans is the maximum value f ever reaches during the scan.
Step-by-step walkthrough:
-
Initialization:
n = len(nums) ans = f = 2Both
ansandfstart at2, because any pair of adjacent elements is automatically a valid Fibonacci subarray. This also handles the base case before any checking begins. -
Traverse from index
2:for i in range(2, n):The Fibonacci condition needs two preceding elements, so the first index worth checking is
2. -
State transition: for each
i, apply one of two rules:-
Extend the streak — if
nums[i] == nums[i-1] + nums[i-2], the current element continues the Fibonacci subarray ending ati-1:f = f + 1 ans = max(ans, f)This corresponds to the transition
f[i] = f[i-1] + 1. The answer is updated immediately, so no separate pass is needed. -
Reset the streak — otherwise, the chain breaks, and the longest Fibonacci subarray ending at
iis just the pair(nums[i-1], nums[i]):f = 2
-
-
Return the result:
return ans
Why a single variable suffices:
A full DP formulation would store an array f[i] for every index. However, the transition for f[i] only ever reads f[i-1], so the entire array can be compressed into one scalar — a classic space optimization for linear-recurrence DP.
Trace example with nums = [1, 1, 2, 3, 5, 4]:
i | nums[i] | Condition check | f | ans |
|---|---|---|---|---|
| — | — | initial | 2 | 2 |
| 2 | 2 | 2 == 1 + 1 ✓ | 3 | 3 |
| 3 | 3 | 3 == 1 + 2 ✓ | 4 | 4 |
| 4 | 5 | 5 == 2 + 3 ✓ | 5 | 5 |
| 5 | 4 | 4 != 3 + 5 ✗ | 2 | 5 |
The result is 5, matching the subarray [1, 1, 2, 3, 5].
Complexity analysis:
- Time complexity:
O(n), wherenis the length ofnums. Each element is visited exactly once, and each visit performs constant-time work (one addition, one comparison, and possibly amax). - Space complexity:
O(1). Only two integer variables (fandans) are maintained regardless of input size.
Example Walkthrough
Let's trace the algorithm on a small example: nums = [2, 3, 5, 8, 10, 18, 28].
Setup:
We initialize both variables to 2:
f = 2— the longest Fibonacci subarray ending at the current position (any adjacent pair qualifies).ans = 2— the best length seen so far.
Scanning from index 2:
At each index i, we check whether nums[i] == nums[i-1] + nums[i-2].
Step 1 — i = 2, nums[2] = 5:
Check: 5 == 3 + 2 ✓
The streak extends: f = 3, and ans = max(2, 3) = 3. The current Fibonacci subarray is [2, 3, 5].
Step 2 — i = 3, nums[3] = 8:
Check: 8 == 5 + 3 ✓
The streak extends again: f = 4, ans = 4. The subarray is now [2, 3, 5, 8].
Step 3 — i = 4, nums[4] = 10:
Check: 10 != 8 + 5 ✗
The chain breaks. We reset f = 2, because the pair (8, 10) is still a valid Fibonacci subarray on its own and is the only possible starting point for future streaks. ans stays at 4.
Step 4 — i = 5, nums[5] = 18:
Check: 18 == 10 + 8 ✓
A new streak builds: f = 3. ans = max(4, 3) = 4 — no improvement yet. The current subarray is [8, 10, 18].
Step 5 — i = 6, nums[6] = 28:
Check: 28 == 18 + 10 ✓
The streak grows: f = 4. ans = max(4, 4) = 4. The current subarray is [8, 10, 18, 28].
Summary table:
i | nums[i] | Condition check | f | ans |
|---|---|---|---|---|
| — | — | initial | 2 | 2 |
| 2 | 5 | 5 == 3 + 2 ✓ | 3 | 3 |
| 3 | 8 | 8 == 5 + 3 ✓ | 4 | 4 |
| 4 | 10 | 10 != 8 + 5 ✗ | 2 | 4 |
| 5 | 18 | 18 == 10 + 8 ✓ | 3 | 4 |
| 6 | 28 | 28 == 18 + 10 ✓ | 4 | 4 |
Result: ans = 4.
Two distinct Fibonacci subarrays of length 4 exist — [2, 3, 5, 8] and [8, 10, 18, 28] — and the single break at index 4 correctly separated them. Notice how the reset to 2 (rather than 0) allowed the second streak to be counted accurately, since the pair (8, 10) immediately seeded a new valid run. The entire computation used one pass and two integer variables, confirming the O(n) time and O(1) space behavior.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def longestSubarray(self, nums: List[int]) -> int:
6 n = len(nums)
7 # Any subarray of length 2 trivially satisfies the condition,
8 # so both the answer and the current streak start at 2.
9 ans = cur_len = 2
10 # Iterate from the third element onward, checking whether each
11 # element equals the sum of its two immediate predecessors.
12 for i in range(2, n):
13 if nums[i] == nums[i - 1] + nums[i - 2]:
14 # The Fibonacci-like property holds; extend the current streak.
15 cur_len += 1
16 # Update the answer if the current streak is the longest seen.
17 ans = max(ans, cur_len)
18 else:
19 # The property is broken; reset the streak.
20 # The new candidate subarray starts with the last two elements.
21 cur_len = 2
22 return ans
231class Solution {
2 /**
3 * Finds the length of the longest contiguous subarray in which every element
4 * (starting from the third one) equals the sum of the previous two elements,
5 * i.e., the longest Fibonacci-like subarray.
6 *
7 * @param nums the input array of integers
8 * @return the length of the longest such subarray (at least 2 by definition)
9 */
10 public int longestSubarray(int[] nums) {
11 // Current length of the Fibonacci-like subarray ending at index i.
12 // Any two adjacent elements trivially form a valid subarray of length 2.
13 int currentLength = 2;
14
15 // Maximum length found so far.
16 int maxLength = currentLength;
17
18 // Start from the third element, checking the Fibonacci-like condition.
19 for (int i = 2; i < nums.length; ++i) {
20 if (nums[i] == nums[i - 1] + nums[i - 2]) {
21 // The condition holds, so extend the current subarray.
22 ++currentLength;
23 // Update the maximum length if the current one is longer.
24 maxLength = Math.max(maxLength, currentLength);
25 } else {
26 // The condition breaks; restart counting from the last two elements.
27 currentLength = 2;
28 }
29 }
30
31 return maxLength;
32 }
33}
341class Solution {
2public:
3 int longestSubarray(vector<int>& nums) {
4 // Any two adjacent elements form a valid Fibonacci-like prefix,
5 // so the minimum length of such a subarray is 2.
6 int currentLength = 2; // Length of the current Fibonacci-like subarray
7 int maxLength = 2; // Maximum length found so far
8
9 // Start from index 2, since the first two elements are always valid
10 for (int i = 2; i < nums.size(); ++i) {
11 if (nums[i] == nums[i - 1] + nums[i - 2]) {
12 // Current element extends the Fibonacci-like subarray
13 ++currentLength;
14 maxLength = max(maxLength, currentLength);
15 } else {
16 // The property is broken; restart with the last two elements
17 currentLength = 2;
18 }
19 }
20
21 return maxLength;
22 }
23};
241/**
2 * Finds the length of the longest contiguous subarray in which every element
3 * (starting from the third position of the subarray) equals the sum of the
4 * two elements immediately before it (i.e., a Fibonacci-like subarray).
5 *
6 * @param nums - The input array of numbers (assumed to have length >= 2)
7 * @returns The length of the longest valid subarray
8 */
9function longestSubarray(nums: number[]): number {
10 // Current length of the valid subarray ending at index i.
11 // Any two adjacent elements trivially form a valid subarray of length 2.
12 let currentLength: number = 2;
13
14 // The maximum subarray length found so far.
15 let maxLength: number = currentLength;
16
17 // Iterate from the third element, checking the Fibonacci-like condition.
18 for (let i = 2; i < nums.length; ++i) {
19 if (nums[i] === nums[i - 1] + nums[i - 2]) {
20 // The condition holds: extend the current subarray
21 // and update the answer if it becomes the longest.
22 maxLength = Math.max(maxLength, ++currentLength);
23 } else {
24 // The condition breaks: restart counting from the
25 // last two elements (minimum valid length is 2).
26 currentLength = 2;
27 }
28 }
29
30 return maxLength;
31}
32Time and Space Complexity
-
Time Complexity:
O(n), wherenis the length of the arraynums. The code iterates through the array exactly once with a singleforloop from index2ton - 1. Each iteration performs only constant-time operations: a comparisonnums[i] == nums[i - 1] + nums[i - 2], an incrementf = f + 1, amaxcomputation, or a resetf = 2. -
Space Complexity:
O(1). The algorithm only uses a fixed number of variables (n,ans,f, and the loop indexi), regardless of the input size. No additional data structures are allocated.
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Ignoring arrays with fewer than two elements
The most dangerous trap in this solution is the initialization ans = cur_len = 2. This silently assumes the array has at least two elements. If nums has length 1 (or is empty), the loop never executes and the function returns 2 — which is impossible, since no subarray of length 2 even exists.
# Buggy behavior: Solution().longestSubarray([7]) # returns 2, but the correct answer is 1 Solution().longestSubarray([]) # returns 2, but the correct answer is 0
Whether this matters depends on the problem's constraints. If the constraints guarantee n >= 2, the code is fine — but copying this pattern to a problem (or test environment) without that guarantee produces wrong answers that are easy to miss because the code runs without raising any error.
Fix: guard the small cases explicitly, or initialize from the array length:
class Solution:
def longestSubarray(self, nums: List[int]) -> int:
n = len(nums)
if n < 2:
return n # 0 for empty array, 1 for single element
ans = cur_len = 2
for i in range(2, n):
if nums[i] == nums[i - 1] + nums[i - 2]:
cur_len += 1
ans = max(ans, cur_len)
else:
cur_len = 2
return ans
An equivalent one-line alternative is ans = cur_len = min(n, 2), which collapses all the edge cases into the initialization itself.
2. Resetting the streak to 1 instead of 2
When the chain breaks at index i, the pair (nums[i-1], nums[i]) is still a valid Fibonacci subarray of length 2 — the broken condition only invalidates the element before that pair. Writing cur_len = 1 on reset undercounts every subsequent streak by one:
# Wrong reset: cur_len = 1 # loses the element nums[i-1], which still belongs to the new window
With nums = [9, 1, 2, 3, 5], the break never happens after index 1, but with nums = [9, 9, 1, 2, 3] the reset at i = 2 followed by extensions would yield 3 instead of the correct 4 ([9, 1, ...] is broken, but [?, 1, 2, 3] should count 1, 2, 3 plus the element before — i.e., the streak ending at index 4 covers [9, 1, 2, 3]... actually 1+2=3 and 9+1≠2, so the valid window is [1, 2, 3] extended from the pair (9, 1)? No — the pair is (nums[i-1], nums[i]) = (9, 1) at the break, then 2 == 9 + 1? No). The subtlety itself illustrates the pitfall: the reset value encodes which two elements seed the next window, and getting it wrong shifts every later count. The invariant to remember: after a reset, the window ending at i always contains exactly the last two elements, hence cur_len = 2.
3. Updating the answer only when the condition succeeds — but in the wrong place
In this code ans = max(ans, cur_len) lives inside the if branch, which is correct only because cur_len never exceeds its previous maximum after a reset (it drops to 2, and ans >= 2 always). A common refactoring mistake is to move the update after the loop only (return max(ans, cur_len) with no in-loop update is fine, but return cur_len is not), or to restructure the branches and forget the update entirely. Safest habit: update ans unconditionally at the end of each iteration —
for i in range(2, n):
cur_len = cur_len + 1 if nums[i] == nums[i - 1] + nums[i - 2] else 2
ans = max(ans, cur_len)
This version cannot miss an update regardless of how the branches are later modified.
4. Confusing "subarray" with "subsequence"
The classic LeetCode problem 873 (Length of Longest Fibonacci Subsequence) asks for a subsequence, which permits skipping elements and requires a hash-map DP in O(n²). Pattern-matching to that problem and reaching for the heavyweight solution here wastes both time and memory — contiguity is what makes the O(n) single-pass scan valid. Conversely, applying this linear scan to the subsequence variant produces wrong answers. Always confirm which variant the problem is asking before choosing the technique.
5. Integer overflow when porting to other languages
Python integers are arbitrary-precision, so nums[i - 1] + nums[i - 2] can never overflow. Translating this line verbatim to C++/Java with int operands can overflow when both values are near 2^31 - 1, turning the sum negative and corrupting the comparison. Use a wider type (long long / long) for the sum in those languages.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhat's the output of running the following function using input [30, 20, 10, 100, 33, 12]?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
81public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
121class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!