LeetCode 873. 最长的斐波那契子序列的长度
题目描述


题意分析
从严格递增的正整数数组中选出最长子序列,使从第三项开始,每项都等于前两项之和。可以跳过元素,但不能改变下标顺序;长度至少为 3 才合法,不存在时返回 0。
解法:两数结尾动态规划
核心思路
[!blue]
下一项由前两项共同决定,只记录最后一个位置不足以判断能否延长。因此定义
dp[j][i]为以arr[j]、arr[i]作为最后两项的最长长度,其中j < i。任意两项都能作为起点,先把状态设为 2,但它还不是合法答案。若这两项之前还有一项,它只能是
arr[i] - arr[j]。数组严格递增,每个值只有一个下标,可用哈希表直接找到候选位置k。只有k < j,才能把arr[i]接到以k、j结尾的子序列后,得到dp[j][i] = dp[k][j] + 1;否则状态保持为 2。前驱唯一,因此不需要枚举第三个下标或比较多个转移。按最后位置
i递增计算,依赖的dp[k][j]已经完成;只在成功延长时更新答案,便能保证无解时仍返回 0。
解题步骤
- 建立
index[value] = 下标,创建二维状态数组,答案ans初始化为 0。- 依次枚举最后位置
i,再枚举0 <= j < i,令dp[j][i] = 2。- 查询
prev = arr[i] - arr[j]的下标k;存在且k < j时,令dp[j][i] = dp[k][j] + 1并更新ans。- 所有下标对处理完后返回
ans。
代码实现
class Solution {
public int lenLongestFibSubseq(int[] arr) {
int n = arr.length;
HashMap<Integer, Integer> index = new HashMap<>();
for (int i = 0; i < n; i++) {
index.put(arr[i], i);
}
int[][] dp = new int[n][n];
int ans = 0;
for (int i = 0; i < n; i++) {
// 外层结尾位置递增,所需更早结尾的状态已经完成
for (int j = 0; j < i; j++) {
// 两项只作为递推基线,合法答案至少包含三项
dp[j][i] = 2;
int prev = arr[i] - arr[j];
Integer k = index.get(prev);
// 命中值还需保证前驱下标更早,不能重复使用当前项
if (k != null && k < j) {
dp[j][i] = dp[k][j] + 1;
ans = Math.max(ans, dp[j][i]);
}
}
}
return ans;
}
}
func lenLongestFibSubseq(arr []int) int {
n := len(arr)
index := make(map[int]int)
for i, num := range arr {
index[num] = i
}
dp := make([][]int, n)
for i := 0; i < n; i++ {
dp[i] = make([]int, n)
}
ans := 0
for i := 0; i < n; i++ {
// 外层结尾位置递增,所需更早结尾的状态已经完成
for j := 0; j < i; j++ {
// 两项只作为递推基线,合法答案至少包含三项
dp[j][i] = 2
prev := arr[i] - arr[j]
// 命中值还需保证前驱下标更早,不能重复使用当前项
if k, ok := index[prev]; ok && k < j {
dp[j][i] = dp[k][j] + 1
if dp[j][i] > ans {
ans = dp[j][i]
}
}
}
}
return ans
}
复杂度分析
- 时间复杂度:期望 $O(n^2)$,其中
n是数组长度;每个下标对做一次哈希查询。- 空间复杂度:$O(n^2)$,二维状态占主要空间,索引表另需 $O(n)$。
关键点总结
[!green]
- 用最后两项定义状态,才能确定唯一的前驱值。
- 严格递增保证值与下标一一对应,但查到前驱后仍要检查
k < j。- 长度 2 用于开始递推,只有延长到至少 3 才更新答案。
易错点总结
[!yellow]
- 只检查前驱值存在,可能复用
j位置或选到它后面的元素,破坏子序列顺序。- 从默认值 0 开始转移会低估长度,每个二元状态都要先设为 2。
- 把长度 2 计入答案,会在没有合法子序列时错误返回 2。
- 枚举顺序必须保证最后位置更早的状态先完成,不能在
dp[k][j]尚未计算时读取它。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 300. 最长递增子序列 | 中等 | 同样选取保持下标顺序的最长子序列,本题下一项由前两项之和确定,需要成对状态。 |
| 1027. 最长等差数列 | 中等 | 同样以最后两个元素或差值确定可延续条件,原题保持固定差,本题满足斐波那契加法关系。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!