{x}
blog image

Length of Longest Fibonacci Subsequence

A sequence x1, x2, ..., xn is Fibonacci-like if:

  • n >= 3
  • xi + xi+1 == xi+2 for all i + 2 <= n

Given a strictly increasing array arr of positive integers forming a sequence, return the length of the longest Fibonacci-like subsequence of arr. If one does not exist, return 0.

A subsequence is derived from another sequence arr by deleting any number of elements (including none) from arr, without changing the order of the remaining elements. For example, [3, 5, 8] is a subsequence of [3, 4, 5, 6, 7, 8].

 

Example 1:

Input: arr = [1,2,3,4,5,6,7,8]
Output: 5
Explanation: The longest subsequence that is fibonacci-like: [1,2,3,5,8].

Example 2:

Input: arr = [1,3,7,11,12,14,18]
Output: 3
Explanation: The longest subsequence that is fibonacci-like: [1,11,12], [3,11,14] or [7,11,18].

 

Constraints:

  • 3 <= arr.length <= 1000
  • 1 <= arr[i] < arr[i + 1] <= 109

Solution Explanation: Length of Longest Fibonacci Subsequence

This problem asks us to find the length of the longest Fibonacci-like subsequence within a given strictly increasing array of positive integers. A Fibonacci-like subsequence is defined as a subsequence where each element (starting from the third) is the sum of the two preceding elements.

Approach: Dynamic Programming

The most efficient approach is using dynamic programming. We'll build a table to store the lengths of Fibonacci-like subsequences.

1. Data Structures:

  • f[i][j] : A 2D array (or matrix) where f[i][j] represents the length of the longest Fibonacci-like subsequence ending with arr[i] and arr[j].
  • d : A hash table (dictionary or map) to store the index of each element in arr for fast lookups. This significantly optimizes the search for the third element in the Fibonacci-like subsequence.

2. Initialization:

  • Initialize f[i][j] to 2 for all i > j, because any two consecutive numbers in arr can form the beginning of a Fibonacci-like subsequence of length 2.
  • Populate d with the element and its index from arr.

3. Iteration:

The core logic lies in iterating through the array. For each pair (arr[i], arr[j]) with i > j, we check if arr[i] - arr[j] exists in the array (arr). If it does, let's say at index k, and k < j (to ensure the subsequence is strictly increasing), then we have found a potential Fibonacci-like subsequence. We update f[i][j] to be the maximum of its current value and f[j][k] + 1. We add 1 because we've extended the subsequence by one element (arr[i]).

4. Result:

The maximum value in the f array represents the length of the longest Fibonacci-like subsequence.

Time and Space Complexity

  • Time Complexity: O(n^2), where n is the length of the input array arr. This is because of the nested loops iterating through all pairs of elements.
  • Space Complexity: O(n^2) to store the f matrix. The hash table d takes O(n) space.

Code Examples (Python and Java):

Python:

class Solution:
    def lenLongestFibSubseq(self, arr: List[int]) -> int:
        n = len(arr)
        f = [[2 if i > j else 0 for j in range(n)] for i in range(n)]  # Initialize f
        d = {x: i for i, x in enumerate(arr)} # Hash table for efficient lookup
        ans = 0
        for i in range(2, n):
            for j in range(1, i):
                t = arr[i] - arr[j]
                if t in d and d[t] < j: # Check if t exists and k < j
                    f[i][j] = f[j][d[t]] + 1 # Update f[i][j]
                    ans = max(ans, f[i][j]) # Update the overall maximum length
        return ans if ans > 2 else 0 # Return 0 if no Fibonacci-like subsequence of length >=3 found

Java:

import java.util.HashMap;
import java.util.Map;
 
class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        int n = arr.length;
        int[][] f = new int[n][n]; // Initialize f
        Map<Integer, Integer> d = new HashMap<>(); // Hash table for efficient lookup
        int ans = 0;
        for (int i = 0; i < n; i++) {
            d.put(arr[i], i);
            for (int j = 0; j < i; j++) {
                f[i][j] = 2;
            }
        }
        for (int i = 2; i < n; i++) {
            for (int j = 1; j < i; j++) {
                int t = arr[i] - arr[j];
                Integer k = d.get(t);
                if (k != null && k < j) {  // Check if t exists and k < j
                    f[i][j] = f[j][k] + 1; // Update f[i][j]
                    ans = Math.max(ans, f[i][j]); // Update the overall maximum length
                }
            }
        }
        return ans > 2 ? ans : 0; // Return 0 if no Fibonacci-like subsequence of length >=3 is found
 
    }
}

The code in other languages (C++, Go, TypeScript, Rust, JavaScript) follows the same dynamic programming logic, just adapted to the specific syntax and data structures of that language. The key components remain the same: the f matrix for DP, the d hash table for efficient lookups, and the nested loops to find and extend Fibonacci-like subsequences.