问题描述
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].)
Example 1:
Input: [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: [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].
Note:
3 <= A.length <= 1000
1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9
- (The time limit has been reduced by 50% for submissions in Java, C, and C++.)
思路
这道题目主要意思就是给我们一个数列,要我们找出里面最长的斐波那契子序列
(可以不连续)。这道题可以用动态规划
来解决,但有一点很重要,就是 dp 数组的 i , j 在这道题目中就不能是一个区间的头和尾了,而是表示结尾两个数的下标,比如 dp[1][3] 就是表示以 A[1], A[3] 结尾的子序列。我们可以遍历 i, j,再从 j 往后找 k,看 A[k] 加入 A[i] , A[j] 结尾的数列后能否构成斐波那契数列,如果能构成斐波那契数列,则以 A[j] , A[k] 结尾的fib数列的长度 = 以A[i], A[j] 结尾的fib数列长度 + 1
,即dp[j][k] = dp[i][j] + 1
。
class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
// 数组A的大小
int n = A.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
// 初始的最小长度是2 (3个数才会构成一个斐波那契数列)
int max = 2;
// 初始化dp数组,任意两个数结尾的数列长度初始化为2
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
dp[i][j] = 2;
}
}
// 对以A[i],A[j]结尾的数列,从j往后找k,看A[k]加入A[i],A[j]结尾的数列后能否构成斐波那契数列
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
for(int k = j + 1; k < n; k++){
if(A[k] == A[i] + A[j]){
// 如果能构成斐波那契数列,则以A[j],A[k]结尾的数列的长度 = 以A[i],A[j]结尾的数列长度+1
dp[j][k] = dp[i][j] + 1;
// 更新 max
if(dp[j][k] > max){
max = dp[j][k];
}
}
}
}
}
// 如果max还是2的话,证明不存在斐波那契子序列,则返回0
if(max == 2){max = 0;}
return max;
}
};
这时一开始写的,但很明显会超时,A的长度最长会达到1000,而上面的代码里面有三重 for 循环,时间复杂度是 O ( n 3 ) O(n^3) O(n3)。
我们可以使用map
来降低时间复杂度。我们可以只遍历 j 和 k,得到 num = A[k] - A[j],判断在 j 之前是否有 A[i] == num,如果有的话,则能加长斐波那契子序列。判断 j 之前是否有 A[i] == num 可以用 map 来查找。
class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
int n = A.size();
// 先把每个元素都存到map里面
map<int, int> store;
for(int i = 0; i < n; i++){
store[A[i]] = i + 1; // 避免0的干扰
}
vector<vector<int>> dp(n, vector<int>(n, 2));
int max = 0;
for(int j = 0; j < n; j++){
for(int k = j + 1; k < n; k++){
// 查找加入A[k]是否可以构成斐波那契数列
int num = A[k] - A[j];
if(num >= A[j]){
// 因为差值都比A[j]大了,所以不可能有斐波那契数列
continue;
}
else{
if(store[num] != 0){ // 证明差值出现在之前的序列中
// 从map中取出index,然后更新dp数组
dp[j][k] = dp[store[num]-1][j] + 1;
if(dp[j][k] > max){
max = dp[j][k];
}
}
}
}
}
return max;
}
};
这样写之后就不存在超时的问题了,然而新的问题出现了,Memory Limit Exceeded。
这是因为自己使用了不好的方法来判断 num 是否存在于 store 中。我首先将 A 中的每个元素都作为 store 的 key,每个 key 的 value 都是数字对应的下标+1,所以就不存在有 0 的 value了,不会影响到我后面用 store[num] 是否为 0 来判断 num 是否存在于数组中。但是每次用 if(store[num] != 0)
就会自动地把 num 加到 map 里面去,这样就会造成内存超限。所以还是要使用find
,之后判断返回的迭代器是否等于 store.end()来判断 num 是否存在于 store 中。
class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
int n = A.size();
// 先把每个元素都存到map里面
map<int, int> store;
for(int i = 0; i < n; i++){
store[A[i]] = i; // 避免0的干扰
}
vector<vector<int>> dp(n, vector<int>(n, 2));
int max = 0;
for(int j = 0; j < n; j++){
for(int k = j + 1; k < n; k++){
// 查找加入A[k]是否可以构成斐波那契数列
int num = A[k] - A[j];
if(num >= A[j]){
// 因为差值都比A[j]大了,所以不可能有斐波那契数列
// 证明我们需要将j后移
break;
}
else{
std::map<int, int>::iterator iter = store.find(num);
if(iter == store.end()){continue;}
else{
dp[j][k] = dp[store[num]][j] + 1;
if(dp[j][k] > max){
max = dp[j][k];
}
}
}
}
}
return max;
}
};