Given a strictly increasing array of positive integers arr
, your task is to find the length of the longest subsequence of arr
that forms a Fibonacci-like sequence. A sequence is Fibonacci-like if:
X1, X2, ..., Xk
, Xi = Xi-1 + Xi-2
for all i ≥ 3
.
Return the length of the longest such subsequence in arr
. If one does not exist, return 0
.
Constraints:
3 ≤ arr.length ≤ 1000
1 ≤ arr[i] < 10^9
arr
are unique and strictly increasing.The problem asks us to look for the longest subsequence that behaves like a Fibonacci sequence. At first glance, you might think of checking all possible subsequences, but that's computationally expensive because the number of subsequences grows exponentially with the array size.
Instead, we notice that any Fibonacci-like sequence is uniquely determined by its first two numbers. Once those are fixed, the rest of the sequence (if possible) is determined. So, for each pair of numbers in arr
, we can try to build the longest Fibonacci-like sequence starting with that pair.
To check if a number exists in the array, we need fast lookups. This suggests using a set or a hash map. We also need to avoid recomputation, so dynamic programming or memoization can help us here.
We will use a dynamic programming approach with hash maps for fast lookups:
arr
to its index. This allows us to efficiently check if a particular sum exists in the array.
dp[i][j]
, where dp[i][j]
represents the length of the longest Fibonacci-like subsequence ending with arr[i]
and arr[j]
(with i < j
).
(i, j)
with i < j
, compute the possible previous number in the sequence as arr[j] - arr[i]
. If this number exists in the array at index k < i
, then dp[i][j] = dp[k][i] + 1
. Otherwise, set dp[i][j] = 2
(the minimal length for a sequence starting with this pair).
This approach efficiently finds the longest Fibonacci-like subsequence by leveraging dynamic programming and hash maps for fast lookups.
Let's walk through an example with arr = [1, 3, 7, 11, 12, 14, 18]
:
(1, 11)
, we check if 11 - 1 = 10
exists in arr
. It doesn't, so we move on.
(3, 11)
: 11 - 3 = 8
not in arr
.
(7, 11)
: 11 - 7 = 4
not in arr
.
(7, 14)
: 14 - 7 = 7
(found at index 2, but index must be less than 2, which is not possible here), so skip.
(11, 18)
: 18 - 11 = 7
(7 is at index 2, which is less than 3). Now, check dp[2][3]
, which is 2 by default, so dp[3][6] = 3
.
(7, 11)
and then (11, 18)
, you can chain the sequence: 7, 11, 18.
The answer for this example is 3.
O(2^n)
, which is not feasible for n
up to 1000.
(i, j)
, so O(n^2)
pairs.
O(n^2)
O(n^2)
for the DP table, plus O(n)
for the hash map.
To solve the Length of Longest Fibonacci Subsequence problem, we leveraged the fact that a Fibonacci-like sequence is determined by its first two elements. By using a dynamic programming table and a hash map for fast lookups, we efficiently built up possible sequences and tracked the maximum length found. The approach is both time and space efficient for the given constraints, and demonstrates the power of combining DP with hash-based data structures.
class Solution:
def lenLongestFibSubseq(self, arr):
index = {x: i for i, x in enumerate(arr)}
n = len(arr)
dp = [[2] * n for _ in range(n)]
maxlen = 0
for j in range(n):
for i in range(j):
prev = arr[j] - arr[i]
k = index.get(prev)
if k is not None and k < i:
dp[i][j] = dp[k][i] + 1
maxlen = max(maxlen, dp[i][j])
return maxlen if maxlen >= 3 else 0
class Solution {
public:
int lenLongestFibSubseq(vector<int>& arr) {
int n = arr.size();
unordered_map<int, int> index;
for (int i = 0; i < n; ++i) index[arr[i]] = i;
vector<vector<int>> dp(n, vector<int>(n, 2));
int maxlen = 0;
for (int j = 0; j < n; ++j) {
for (int i = 0; i < j; ++i) {
int prev = arr[j] - arr[i];
if (index.count(prev)) {
int k = index[prev];
if (k < i) {
dp[i][j] = dp[k][i] + 1;
maxlen = max(maxlen, dp[i][j]);
}
}
}
}
return maxlen >= 3 ? maxlen : 0;
}
};
class Solution {
public int lenLongestFibSubseq(int[] arr) {
int n = arr.length;
Map<Integer, Integer> index = new HashMap<>();
for (int i = 0; i < n; ++i) index.put(arr[i], i);
int[][] dp = new int[n][n];
for (int[] row : dp) Arrays.fill(row, 2);
int maxlen = 0;
for (int j = 0; j < n; ++j) {
for (int i = 0; i < j; ++i) {
int prev = arr[j] - arr[i];
if (index.containsKey(prev)) {
int k = index.get(prev);
if (k < i) {
dp[i][j] = dp[k][i] + 1;
maxlen = Math.max(maxlen, dp[i][j]);
}
}
}
}
return maxlen >= 3 ? maxlen : 0;
}
}
var lenLongestFibSubseq = function(arr) {
const n = arr.length;
const index = new Map();
for (let i = 0; i < n; ++i) index.set(arr[i], i);
const dp = Array.from({length: n}, () => Array(n).fill(2));
let maxlen = 0;
for (let j = 0; j < n; ++j) {
for (let i = 0; i < j; ++i) {
let prev = arr[j] - arr[i];
if (index.has(prev)) {
let k = index.get(prev);
if (k < i) {
dp[i][j] = dp[k][i] + 1;
maxlen = Math.max(maxlen, dp[i][j]);
}
}
}
}
return maxlen >= 3 ? maxlen : 0;
};