一刷294-dp+双指针-剑指 Offer II 093. 最长斐波那契数列m(同:873. 最长的斐波那契子序列的长度)

题目:
如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:
1、n >= 3
2、对于所有 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
----------------------
思路:
每个斐波那契数列都可以由其最后两位数字精准定位,
例如:定义定位数组为dp[][],对于 arr = [1,2,3,4,5,6,7,8] ,
dp[2][3]表示数列[1,2,3]、dp[3][5]表示数列[1,2,3,5]、dp[5][8]表示数列[1,2,3,5,8];

每个dp元素的值,代表它所定位的数列的长度,例如上面的 dp[2][3]=3 , dp[3][5]=4 , dp[5][8]=5;
则每遍历到一个新的数组元素i,就对dp数组中的新的一列进行赋值

赋值规则:
如果两个前置元素j、k之前并没有形成斐波那契子数列(即dp[j][k]==0),
那就将当前位置存储的斐波那契数列长度dp[k][i]修改为3

如果前置元素j、k之前作为某个斐波那契数列的最后两位元素存在(即dp[j][k]==n,n>=3),
如果这时dp[k][i]的值小于 n+1 就将当前位置存储的斐波那契数列长度dp[k][i]修改为 n+1 ,否则不变
(这一步很容易忽略,动态规划的过程中需要保证当前位置的值是当前对应数组元素i时,能取到的最大值,
否则可能会出现后来得到的较小dp[k][i]值覆盖之前较大dp[k][i]值的情况)
这一步也保证了每个斐波那契数列由其最后两位数字精准定位的正确性!

为了取全局最大的数列长度,在每次刷新dp某元素的值时,都要比较是否大于当前存储的最大值。
本题我一开始是采用暴力三重循环的方式遍历每个节点,结果会出现超时的情况,
所以改用双指针的方式消去一重循环
-----------------------
时间复杂度:O()
空间复杂度:O()
n为数组arr的长度
-----------------
class Solution {//dp[2][3]表示数列[1,2,3]
    public int lenLongestFibSubseq(int[] arr) {
        int len = arr.length;//正整数数组形成的序列的长度
        int res = 0;//结果集
        int[][] dp = new int[len][len];//dp: 二维数组的值,代表它所定位的数列的长度
        for (int i = 2; i < len; i++) {
            int j = 0; 
            int k = i - 1;
            while (j < k) {//双指针
                if (arr[j] + arr[k] == arr[i]) {//满足条件
                    if (dp[j][k] == 0) {//之前未形成斐波那契
                        dp[k][i] = 3;//j, k, i 三个数,所以长度赋值为3
                    }else {
                        dp[k][i] = Math.max(dp[j][k] + 1, dp[k][i]);
                    }
                    res = Math.max(res, dp[k][i]);//将结果存入res
                    j++;//更新
                    k--;
                }else if (arr[j] + arr[k] < arr[i]) {
                    j++;
                }else {
                    k--;
                }
            }
        }
        return res;
    }
}

LC

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值