Medium
题目描述
如果序列 x1, x2, …, xn 满足下列条件,就说它是斐波那契式的:
- n >= 3
- 对于所有 i + 2 <= n,都有 xi + xi+1 == xi+2
给定一个严格递增的正整数数组 arr,返回 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,就返回 0。
子序列是从原序列 arr 中派生出来的,它从 arr 中删去任意数量的元素(也可以不删除),但不改变其余元素的顺序。例如,[3, 5, 8] 是 [3, 4, 5, 6, 7, 8] 的一个子序列。
示例 1:
输入: arr = [1,2,3,4,5,6,7,8]
输出: 5
解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。
示例 2:
输入: arr = [1,3,7,11,12,14,18]
输出: 3
解释: 最长的斐波那契式子序列有 [1,11,12]、[3,11,14] 以及 [7,11,18] 。
约束条件:
- 3 <= arr.length <= 1000
- 1 <= arr[i] < arr[i + 1] <= 10^9
解题思路
这道题可以使用动态规划来解决。核心思想是以每两个数作为斐波那契数列的前两个数,然后尝试向后扩展。
解法一:哈希表 + 动态规划(推荐)
- 使用哈希表记录每个数字的下标位置,便于快速查找
- 用
dp[i][j]表示以arr[i]和arr[j]为结尾的斐波那契数列的长度(其中 i < j) - 对于每对 (i, j),查找是否存在
arr[k] = arr[i] + arr[j],如果存在则更新dp[j][k] - 状态转移:
dp[j][k] = max(dp[j][k], dp[i][j] + 1)
解法二:暴力枚举 对于每对可能的起始数字,不断查找下一个斐波那契数,直到找不到为止。这种方法时间复杂度较高。
第一种方法更加优雅且效率更高,通过动态规划避免了重复计算,是推荐的解法。
时间复杂度为 O(n²),空间复杂度为 O(n²),其中 n 是数组长度。
代码实现
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;
}
// dp[i][j] 表示以 arr[i] 和 arr[j] 结尾的斐波那契数列长度
vector<vector<int>> dp(n, vector<int>(n, 0));
int maxLen = 0;
for (int j = 1; j < n; j++) {
for (int i = 0; i < j; i++) {
int target = arr[j] - arr[i];
if (target < arr[i] && index.count(target)) {
int k = index[target];
dp[i][j] = dp[k][i] + 1;
} else {
dp[i][j] = 2;
}
maxLen = max(maxLen, dp[i][j]);
}
}
return maxLen >= 3 ? maxLen : 0;
}
};
class Solution:
def lenLongestFibSubseq(self, arr: List[int]) -> int:
n = len(arr)
index = {val: i for i, val in enumerate(arr)}
# dp[i][j] 表示以 arr[i] 和 arr[j] 结尾的斐波那契数列长度
dp = [[0] * n for _ in range(n)]
max_len = 0
for j in range(1, n):
for i in range(j):
target = arr[j] - arr[i]
if target < arr[i] and target in index:
k = index[target]
dp[i][j] = dp[k][i] + 1
else:
dp[i][j] = 2
max_len = max(max_len, dp[i][j])
return max_len if max_len >= 3 else 0
public class Solution {
public int LenLongestFibSubseq(int[] arr) {
int n = arr.Length;
Dictionary<int, int> index = new Dictionary<int, int>();
// 建立数值到索引的映射
for (int i = 0; i < n; i++) {
index[arr[i]] = i;
}
// dp[i][j] 表示以 arr[i] 和 arr[j] 结尾的斐波那契数列长度
int[,] dp = new int[n, n];
int maxLen = 0;
for (int j = 1; j < n; j++) {
for (int i = 0; i < j; i++) {
int target = arr[j] - arr[i];
if (target < arr[i] && index.ContainsKey(target)) {
int k = index[target];
dp[i, j] = dp[k, i] + 1;
} else {
dp[i, j] = 2;
}
maxLen = Math.Max(maxLen, dp[i, j]);
}
}
return maxLen >= 3 ? maxLen : 0;
}
}
/**
* @param {number[]} arr
* @return {number}
*/
var lenLongestFibSubseq = function(arr) {
const n = arr.length;
const index = new Map();
// 建立数值到索引的映射
for (let i = 0; i < n; i++) {
index.set(arr[i], i);
}
// dp[i][j] 表示以 arr[i] 和 arr[j] 结尾的斐波那契数列长度
const dp = Array.from({length: n}, () => new Array(n).fill(0));
let maxLen = 0;
for (let j = 1; j < n; j++) {
for (let i = 0; i < j; i++) {
const target = arr[j] - arr[i];
if (target < arr[i] && index.has(target)) {
const k = index.get(target);
dp[i][j] = dp[k][i] + 1;
} else {
dp[i][j] = 2;
}
maxLen = Math.max(maxLen, dp[i][j]);
}
}
return maxLen >= 3 ? maxLen : 0;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 双重循环遍历所有数对,每次操作为O(1) |
| 空间复杂度 | O(n²) | dp数组和哈希表的空间开销 |
相关题目
- . Fibonacci Number (Easy)