32.最长的斐波那契子序列的长度(medium)

1.题目链接:

873. 最长的斐波那契子序列的长度 - 力扣(LeetCode)873. 最长的斐波那契子序列的长度 - 如果序列 x1, x2, ..., x2 满足下列条件,就说它是 斐波那契式 的: * n >= 3 * 对于所有 i + 2 <= n,都有 xi + xi+1 == xi+2给定一个 严格递增 的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果不存在,返回  0 。子序列 是通过从另一个序列 arr 中删除任意数量的元素(包括删除 0 个元素)得到的,同时不改变剩余元素顺序。例如,[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] <= 109https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/description/

2.题目描述:

如果序列 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​
3. 解法(动态规划):
算法思路:
1. 状态表示:
        对于线性 dp ,我们可以用「经验 + 题目要求」来定义状态表示:​

i.

以某个位置为结尾,巴拉巴拉;

ii. 以某个位置为起点,巴拉巴拉。

这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:dp[i]  表示:以 i  位置元素为结尾的「所有子序列」中,最长的斐波那契子数列的长度。​但是这里有一个非常致命的问题,那就是我们无法确定 i 结尾的斐波那契序列的样子。这样就会导致我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定一个斐波那契序列。根据斐波那契数列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样子。

因此,我们修改我们的状态表示为:
dp[i][j]  表示:以 i  位置以及 j  位置的元素为结尾的所有的子序列中,最长的斐波那契子序列的长度。规定一下 i < j 。​

2. 状态转移方程:
nums[i] = b, nums[j] = c ,那么这个序列的前一个元素就是 a = c - b 。我们根a  的情况讨论:​

i.

a  存在,下标为 k ,并且 a < b :此时我们需要以 k  位置以及 i  位置元素为结尾的

最长斐波那契子序列的长度,然后再加上 j  位置的元素即可。于是 dp[i][j] = dp[k][i] + 1

ii.

a  存在,但是 b < a < c :此时只能两个元素自己玩了,dp[i][j] = 2 ;​

iii.

a  不存在:此时依旧只能两个元素自己玩了,dp[i][j] = 2 。​

综上,状态转移方程分情况讨论即可。

优化点:我们发现,在状态转移方程中,我们需要确定 a  元素的下标。因此我们可以在 dp  之前,将所有的「元素 + 下标」绑定在一起,放到哈希表中。​

3. 初始化:
        可以将表里面的值都初始化为 2 。​

4. 填表顺序:

a. 先固定最后一个数;
b. 然后枚举倒数第二个数。​

5. 返回值:
因为不知道最终结果以谁为结尾,因此返回 dp  表中的最大值 ret 。​但是 ret  可能小于 3 ,小于 3  的话说明不存在。​
因此需要判断一下。

Java算法代码

class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
        int  n = arr.length;
        for(int i = 0; i < n; i++) hash.put(arr[i], i);

        int[] [] dp = new int[n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                dp[i][j] = 2;
            }
        }
        int ret = 2;
        for(int j = 2; j < n; j++){
            for(int i = 1; i < j; i++){
                int a = arr[j] - arr[i];
                if(a < arr[i] && hash.containsKey(a)){
                    dp[i][j] = dp[hash.get(a)][i] + 1;

                }
                ret = Math.max(ret,dp[i][j]);
            }
        }
        return ret < 3 ? 0 : ret;
    }
}

运行结果:

动态规划:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值