leetcode-873. 最长的斐波那契子序列的长度【动态规划】

本文探讨如何使用暴力解法和动态规划解决LeetCode问题,通过实例分析求解给定数组中最长斐波那契式子序列的长度,比较两种方法的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

题目

解法一:暴力解法

解法二:动态规划


题目

最长的斐波那契子序列的长度

如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:

n >= 3
对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/length-of-longest-fibonacci-subsequence

解法一:暴力解法

思路

每个斐波那契式的子序列都依靠两个相邻项来确定下一个预期项。例如,对于 2, 5,我们所期望的子序列必定以 7, 12, 19, 31 等继续。

我们可以使用 Set 结构来快速确定下一项是否在数组 A 中。由于这些项的值以指数形式增长,最大值≤110^9  的斐波那契式的子序列最多有 43 项。

 public int lenLongestFibSubseq1(int[] A) {
        int N = A.length;
        Set<Integer> S = new HashSet();// 使用set来存储便于快速查找
        for (int x: A) S.add(x);
        int res = 0;
        for (int i = 0; i < N; ++i)
            for (int j = i+1; j < N; ++j) {
                int x = A[j]; // 假设从pair(A[i], A[j]) 开始
                int y = A[i] + A[j];// y 代表 A[i]->A[j] 的下一个斐波那契数列的元素
                int length = 2;
                while (S.contains(y)) { // 继续向后搜索以pair (A[i], A[j])开始的斐波那契数列
                    // x, y -> y, x+y
                    int tmp = y;
                    y += x;
                    x = tmp;
                    res = Math.max(res, ++length);
                }
            }
        return res >= 3 ? res : 0;
    }

复杂度分析

时间复杂度:O(N^2 log M),其中 N是 A 的长度,M是 A 中的最大值。
空间复杂度:O(N),集合(set)S 使用的空间。


解法二:动态规划

动态规划解题步骤
1. 想清楚原问题与子问题
• 子问题为求解结点 (i, j) 结尾的斐波那契子序列的长度
2. 设计状态
• dp(i,j)结点(i, j)结尾的斐波那契子序列的长度
3. 设计状态转移方程
• if(A[k]+A[i] == A[j]) dp(i, j)=dp(k,i)+1
• 目标值 = max( dp(i,j))
4. 确定边界状态值
• dp(i,j)=2

 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) {
                   // 把(i, j) 对应的位置编码成 (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;
    }

复杂度分析

时间复杂度:O(N^2),其中 N 是 A 的长度。
空间复杂度:O(NlogM),其中 M 是 A 中最大的元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值