Leetcode 873. Length of Longest Fibonacci Subsequence
Table of Contents
Problem Informations
- Problem Index: 873
- Problem Link: Length of Longest Fibonacci Subsequence
- Topics: Array, Hash Table, Dynamic Programming
Problem Description
A sequence $x_1, x_2, \ldots, x_n$ is Fibonacci-like if:
- $n \geq 3$
- $x_i + x_{i+1} = x_{i+2}$ for all $i + 2 \leq 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 \leq arr.length \leq 1000$
- $1 \leq arr[i] < arr[i + 1] \leq 10^9$
Intuition
The problem requires identifying the longest subsequence in a strictly increasing array that exhibits a Fibonacci-like property. Each subsequence term, starting from the third one, must be the sum of the two preceding terms. The constraint that the given array is strictly increasing simplifies identification of potential predecessors needed to form such a sequence.
The challenge is to efficiently determine pairs of numbers that can form valid Fibonacci-like subsequences. A dynamic programming approach is suitable here, as it can build solutions for longer subsequences based on shorter, previously solved subsequences.
Approach
The approach leverages a dynamic programming (DP) technique to systematically explore potential Fibonacci-like subsequences within the array. Here’s a detailed explanation of the algorithm:
Initialization:
- We define a 2D DP table
dp
wheredp[j][i]
will store the length of the longest Fibonacci-like subsequence ending with elements at indicesj
andi
. Initially, each value is set to 2 since any two elements form a trivial subsequence.
- We define a 2D DP table
Iterating through the array:
- We iterate over each possible endpoint
i
for our subsequences, starting from the third element in the array (index 2), since a Fibonacci-like sequence requires at least three elements. - For each
i
, we consider all potential second-to-last elementsj
ranging fromi-1
down to1
.
- We iterate over each possible endpoint
Determining the previous element:
- For each selected pair
(j, i)
, compute the elementneededPrev
that must exist as the element beforej
to maintain the Fibonacci property:arr[k] + arr[j] = arr[i]
. Hence,neededPrev = arr[i] - arr[j]
. - If
neededPrev
is not less thanarr[j]
, break out of the loop since all subsequentj
will yield a largerneededPrev
, violating the increasing order property.
- For each selected pair
Check existence of the previous element:
- Utilize binary search (
lower_bound
) within the bounds of potential indices to check ifneededPrev
exists in the array before indexj
. - If found at index
k
, it impliesarr[k]
forms a valid Fibonacci-like property witharr[j]
andarr[i]
.
- Utilize binary search (
Update the DP table:
- Update
dp[j][i]
asdp[k][j] + 1
, indicating the current length of Fibonacci-like subsequence up toi
. - Track the maximum subsequence length encountered during the iterations as
maxLength
.
- Update
Result Evaluation:
- After considering all possible subsequences, return
maxLength
if it is greater than 2; otherwise, return0
as no valid subsequence longer than two was found.
- After considering all possible subsequences, return
This method ensures that we efficiently explore all potential subsequences and effectively capture the longest one with the desired property while adhering to time complexity constraints.
Code
C++
class Solution {
public:
int lenLongestFibSubseq(vector<int>& arr) {
int n = arr.size();
// Create a 2D DP table initialized to store at least 2 for every pair
vector<vector<int>> dp(n, vector<int>(n, 2));
// Answer variable to keep track of the max length found
int maxLength = 0;
// Iterate over the array from the third element onwards
for (int i = 2; i < n; i++) {
// Iterate from the element just before i till the start of the array
for (int j = i - 1; j > 0; j--) {
// Compute the previous element needed for Fibonacci property
int neededPrev = arr[i] - arr[j];
// If the previous element is not smaller than the current element, break
if (neededPrev >= arr[j]) break;
// Find the position of the previous element in the array
auto it = lower_bound(arr.begin(), arr.begin() + j, neededPrev);
// Check if the previous element exists
if (it != arr.begin() + j && *it == neededPrev) {
int k = it - arr.begin(); // Index of the previous element
// Update the DP table for (j, i)
dp[j][i] = dp[k][j] + 1;
// Update maxLength with the length found
maxLength = max(maxLength, dp[j][i]);
}
}
}
// If the longest length is greater than two, return it; otherwise, return 0
return maxLength > 2 ? maxLength : 0;
}
};
Python
class Solution:
def lenLongestFibSubseq(self, arr):
n = len(arr)
# Create a 2D DP table initialized to store at least 2 for every pair
dp = [[2] * n for _ in range(n)]
# Answer variable to keep track of the max length found
maxLength = 0
# Iterate over the array from the third element onwards
for i in range(2, n):
# Iterate from the element just before i till the start of the array
for j in range(i - 1, 0, -1):
# Compute the previous element needed for Fibonacci property
neededPrev = arr[i] - arr[j]
# If the previous element is not smaller than the current element, break
if neededPrev >= arr[j]:
break
# Find the position of the previous element in the array
it = bisect.bisect_left(arr, neededPrev, 0, j)
# Check if the previous element exists
if it != j and arr[it] == neededPrev:
k = it # Index of the previous element
# Update the DP table for (j, i)
dp[j][i] = dp[k][j] + 1
# Update maxLength with the length found
maxLength = max(maxLength, dp[j][i])
# If the longest length is greater than two, return it; otherwise, return 0
return maxLength if maxLength > 2 else 0
Complexity
Time complexity: $O(n^2 \log n)$
The algorithm involves two nested loops iterating over the array
arr
, with the outer loop running from the third element to the end of the array ($O(n)$ iterations) and the inner loop iterating backward from the current position of the outer loop ($O(n)$ iterations). Inside the inner loop,lower_bound
is called to find the required previous element, which in the worst case involves a binary search on up to $j$ elements, giving it a complexity of $O(\log n)$. Thus, the overall time complexity is $O(n^2 \log n)$.Space complexity: $O(n^2)$
The space complexity is dominated by the 2D dynamic programming table
dp
, which contains $n \times n$ entries. Each entry represents the length of the longest Fibonacci-like subsequence that ends with the elements at indices $j$ and $i$. Therefore, the space complexity is $O(n^2)$.
Disclaimer: All reference materials on this website are sourced from the internet and are intended for learning purposes only. If you believe any content infringes upon your rights, please contact me at csnote.cc@gmail.com, and I will remove the relevant content promptly.
Feedback Welcome: If you notice any errors or areas for improvement in the articles, I warmly welcome your feedback and corrections. Your input will help this blog provide better learning resources. This is an ongoing process of learning and improvement, and your suggestions are valuable to me. You can reach me at csnote.cc@gmail.com.