剑指 Offer II 093. 最长斐波那契数列(中等 动态规划 哈希表 数组)

剑指 Offer II 093. 最长斐波那契数列

如果序列 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/Q91FMA
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

1、找状态转移方程。首先我们知道斐波那契数列的长度至少为3,我们假设我们找到的数列最后一个数字的索引是i,它的前一个数字索引是j,倒数第三个数字索引是k,因为数列是单增的,显然需要满足i > j > k >= 0,那么我们假设f(i, j)表示以i为末尾数字索引j为前一个数字索引的最长斐波那契数列的长度,如果存在索引k的数字是该数列的倒数第三个数的话,就有 f(i, j) = f(j , k) + 1。
2、根据状态转移方程构建dp数组。我们需要考虑f(i, j)和f(j , k),那么构建的dp数组就是二维的,由于斐波那契数列可能有多种组合,所以不能像前几题一样只动态调整两列元素,dp为arr.length * arr.length的大小,dp[i][j] 表示以i为末尾数字索引j为前一个数字索引的最长斐波那契数列的长度,假设该长度是3的话,dp[j][k] 就是2,所以对于无法构成斐波那契数列的索引对来说令其为2。
3、我们在遍历数组的过程中,至少需要找三个数字,索引i、索引j和索引k处的数字,用三重循环来遍历会产生超时,那么我们想办法进行降维,考虑到数组中数字都是不重复的,我们用哈希表来记录数字和对应索引,用二重循环遍历数组的时候,arr[i] - arr[j] 如果在哈希表中就说明可以构成斐波那契数列,这时用 dp[i][j] = dp[j][k] + 1给dp数组添加元素。
4、在遍历过程中记录dp数组中最大的元素最后返回即可,如果最大的元素是2说明数组元素不能构成斐波那契数列返回0。

题解(Java)

class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        Map<Integer, Integer> map = new HashMap<>();
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            map.put(arr[i], i);
        }
        int[][] dp = new int[n][n];
        int ans = 2;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int k = map.getOrDefault(arr[i] - arr[j], -1);
                dp[i][j] = k >= 0 && k < j ? dp[j][k] + 1 : 2;
                ans = Math.max(dp[i][j], ans);
            }
        }
        return ans == 2 ? 0 : ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值