https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/
https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/discuss/152332/Java-clean-DP-O(n2)-time-O(n2)-space
Approach 1: Brute Force with Set
A sequence
X_1, X_2, ..., X_n
is fibonacci-like if:n >= 3
X_i + X_{i+1} = X_{i+2}
for alli + 2 <= n
Given a strictly increasing array
A
of positive integers forming a sequence, find the length of the longest fibonacci-like subsequence of A
. If one does not exist, return 0.
(Recall that a subsequence is derived from another sequence
A
by deleting any number of elements (including none) from A
, without changing the order of the remaining elements. For example, [3, 5, 8]
is a subsequence of [3, 4, 5, 6, 7, 8]
.)
X. https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/discuss/152343/C%2B%2BJavaPython-Check-Pair
https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/discuss/152332/Java-clean-DP-O(n2)-time-O(n2)-space
Then we have
The complexity reduce to
In C++/Java, I use 2D dp and index as key.
In Python, I use value as key.
https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/discuss/152332/Java-clean-DP-O(n2)-time-O(n2)-space
dp[a, b]
represents the length of fibo sequence ends up with (a, b)
Then we have
dp[a, b] = (dp[b - a, a] + 1 ) or 2
The complexity reduce to
O(N^2)
.In C++/Java, I use 2D dp and index as key.
In Python, I use value as key.
public int lenLongestFibSubseq(int[] A) {
int res = 0;
int[][] dp = new int[A.length][A.length];
Map<Integer, Integer> index = new HashMap<>();
for (int j = 0; j < A.length; j++) {
index.put(A[j], j);
for (int i = 0; i < j; i++) {
int k = index.getOrDefault(A[j] - A[i], -1);
dp[i][j] = (A[j] - A[i] < A[i] && k >= 0) ? dp[k][i] + 1 : 2;
res = Math.max(res, dp[i][j]);
}
}
return res > 2 ? res : 0;
}
https://leetcode.com/articles/length-of-longest-fibonacci-subsequence/
Approach 2: Dynamic Programming
- Time Complexity: , where is the length of
A
. - Space Complexity: , where is the largest element of
A
. We can show that the number of elements in a subsequence is bounded by where is the minimum element in the subsequence.
Think of two consecutive terms
A[i], A[j]
in a fibonacci-like subsequence as a single node (i, j)
, and the entire subsequence is a path between these consecutive nodes. For example, with the fibonacci-like subsequence (A[1] = 2, A[2] = 3, A[4] = 5, A[7] = 8, A[10] = 13)
, we have the path between nodes (1, 2) <-> (2, 4) <-> (4, 7) <-> (7, 10)
.
The motivation for this is that two nodes
(i, j)
and (j, k)
are connected if and only if A[i] + A[j] == A[k]
, and we needed this amount of information to know about this connection. Now we have a problem similar to Longest Increasing Subsequence.
Algorithm
Let
longest[i, j]
be the longest path ending in [i, j]
. Then longest[j, k] = longest[i, j] + 1
if (i, j)
and (j, k)
are connected. Since i
is uniquely determined as A.index(A[k] - A[j])
, this is efficient: we check for each j < k
what i
is potentially, and update longest[j, k]
accordingly.- Time Complexity: , where is the length of
A
. - Space Complexity: , where is the largest element of
A
. We can show that the number of elements in a subsequence is bounded by where is the minimum element in the subsequence.
public int lenLongestFibSubseq(int[] A) {
int N = A.length;
Map<Integer, Integer> index = new HashMap();
for (int i = 0; i < N; ++i)
index.put(A[i], i);
Map<Integer, Integer> longest = new HashMap();
int ans = 0;
for (int k = 0; k < N; ++k)
for (int j = 0; j < k; ++j) {
int i = index.getOrDefault(A[k] - A[j], -1);
if (i >= 0 && i < j) {
// Encoding tuple (i, j) as integer (i * N + j)
int cand = longest.getOrDefault(i * N + j, 2) + 1;
longest.put(j * N + k, cand);
ans = Math.max(ans, cand);
}
}
return ans >= 3 ? ans : 0;
}
public int lenLongestFibSubseq(int[] A) {
int n = A.length;
int[][] dp = new int[n][n];
Map<Integer, Integer> pos = new HashMap<>();
for (int i = 0; i < n; i++) {
pos.put(A[i], i);
for (int j = i; j < n; j++) {
dp[i][j] = 2;
}
}
for (int j = 2; j < n; j++) {
for (int i = j - 1; i > 0; i--) {
int prev = A[j] - A[i];
if (prev >= A[i]) {
break;
}
if (!pos.containsKey(prev)) {
continue;
}
dp[i][j] = dp[pos.get(prev)][i] + 1;
}
}
int result = 0;
for (int j = 2; j < n; j++) {
for (int i = 1; i < n - 1; i++) {
if (dp[i][j] > 2) {
result = Math.max(result, dp[i][j]);
}
}
}
return result;
}
For each A[i] find previous 2 elements sum up to A[i],
then it's a classical follow up problem for Two Sum
if 2 elements A[l] and A[r] sum up to A[i]
return the max(all posible dp[r][i])
then it's a classical follow up problem for Two Sum
167. Two Sum II - Input array is sorted
if 2 elements A[l] and A[r] sum up to A[i]
dp[r][i]
: length of longest fibonacchi sequence end with A[r], A[i]dp[r][i] = dp[l][r] + 1
return the max(all posible dp[r][i])
public int lenLongestFibSubseq(int[] A) {
int n = A.length;
int max = 0;
int[][] dp = new int[n][n];
for (int i = 1; i < n; i++) {
int l = 0, r = i - 1;
while (l < r) {
int sum = A[l] + A[r];
if (sum > A[i]) {
r--;
} else if (sum < A[i]) {
l++;
} else {
dp[r][i] = dp[l][r] + 1;
max = Math.max(max, dp[r][i]);
r--;
l++;
}
}
}
return max == 0 ? 0 : max + 2;
}
Time Complexity:
Space Complexity:
O(N^2)
Space Complexity:
O(N^2
- it doesn't work, it returns 3 for {3,2,1}
Every Fibonacci-like subsequence has each two adjacent terms determine the next expected term. For example, with
2, 5
, we expect that the sequence must continue 7, 12, 19, 31
, etc.
We can use a
Set
structure to determine quickly whether the next term is in the array A
or not. Because of the exponential growth of these terms, there are at most 43 terms in any Fibonacci-like subsequence that has maximum value .
Algorithm
For each starting pair
A[i], A[j]
, we maintain the next expected value y = A[i] + A[j]
and the previously seen largest value x = A[j]
. If y
is in the array, then we can then update these values (x, y) -> (y, x+y)
.
Also, because subsequences are only fibonacci-like if they have length 3 or more, we must perform the check
ans >= 3 ? ans : 0
at the end.- Time Complexity: , where is the length of
A
, and is the maximum value ofA
. - Space Complexity: , the space used by the set
S
.
public int lenLongestFibSubseq(int[] A) {
int N = A.length;
Set<Integer> S = new HashSet();
for (int x : A)
S.add(x);
int ans = 0;
for (int i = 0; i < N; ++i)
for (int j = i + 1; j < N; ++j) {
/*
* With the starting pair (A[i], A[j]), y represents the future expected value
* in the fibonacci subsequence, and x represents the most current value found.
*/
int x = A[j], y = A[i] + A[j];
int length = 2;
while (S.contains(y)) {
// x, y -> y, x+y
int tmp = y;
y += x;
x = tmp;
ans = Math.max(ans, ++length);
}
}
return ans >= 3 ? ans : 0;
}