Facebook Pixel

3708. Longest Fibonacci Subarray

MediumArray
LeetCode ↗

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 1 or 2 automatically 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 least 2 when 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 because 11 = 4 + 7 and 18 = 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Simulation / Basic DSA?

This problem maps to Simulation / Basic DSA through a short path in the full flowchart.

Tree orgraph?noSimulation/ directcalculation?yesSimulation /Basic DSA

Scanning for the longest Fibonacci-like consecutive streak is a simple single-pass run-length check with a rolling counter.

Open in Flowchart

Intuition

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 length 2 (the pair nums[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:

  1. Initialization:

    n = len(nums)
    ans = f = 2

    Both ans and f start at 2, because any pair of adjacent elements is automatically a valid Fibonacci subarray. This also handles the base case before any checking begins.

  2. 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.

  3. 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 at i-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 i is just the pair (nums[i-1], nums[i]):

      f = 2
  4. 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]:

inums[i]Condition checkfans
initial22
222 == 1 + 133
333 == 1 + 244
455 == 2 + 355
544 != 3 + 525

The result is 5, matching the subarray [1, 1, 2, 3, 5].

Complexity analysis:

  • Time complexity: O(n), where n is the length of nums. Each element is visited exactly once, and each visit performs constant-time work (one addition, one comparison, and possibly a max).
  • Space complexity: O(1). Only two integer variables (f and ans) 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:

inums[i]Condition checkfans
initial22
255 == 3 + 233
388 == 5 + 344
41010 != 8 + 524
51818 == 10 + 834
62828 == 18 + 1044

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
23
1class 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}
34
1class 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};
24
1/**
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}
32

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the array nums. The code iterates through the array exactly once with a single for loop from index 2 to n - 1. Each iteration performs only constant-time operations: a comparison nums[i] == nums[i - 1] + nums[i - 2], an increment f = f + 1, a max computation, or a reset f = 2.

  • Space Complexity: O(1). The algorithm only uses a fixed number of variables (n, ans, f, and the loop index i), 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 Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What'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
8
1public 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}
12
1class 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}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More