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

题目描述:

如果序列 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

解题分析及思路:

1、dfs

首先这道题给我的第一直觉就是它能够使用dfs进行解答,事实也是如此。

对于数列序列 X_1, X_2, …, X_n,满足X_i + X_{i+1} = X_{i+2},那么只需要每次将前两个满足条件的数值缓存下来,访问下一个元素,如果满足条件更新两个临时值,结果加一,否则跳过该数值,直到遍历完这个数组

其实是很符合dfs的条件的,无奈运行超时,只能放弃,不过思想还是值得大家借鉴的。最长的斐波那契子序列的长度 🔗

2、dp + hash

对于长度为n的数列,需要为其构建一个n ^ 2的二维数组dp,保存其dp[row][col]位置满足斐波那契序列的组数。

对于dp[row][col],满足 arr[row] + arr[col] = num,并且num确实是存在与arr数组中,其下标为index,那么dp[row][col] = dp[col][index] + 1,代表存在一组这样的数满足条件。

另一个问题就来了,就算找到数值num,那么如何通过num判断它是否存在与arr中并且找到其所对应的下标呢?

可以采用map来存arr中每一个元素和该元素对应的下标,元素num作为key,其下标index作为value,那么问题就解决了。

还需要注意两个地方:

  • 因为是严格递增的数组,所以内部循环无需遍历n次,到外部循环的所在的下标停止即可。
  • 因为设置了dp[row][col] 存放的是满足斐波那契序列的组数,然而题目是返回满足斐波那契序列的元素个数,所以元素个数会比组数多2,在返回结果时加2再返回即可。并且最终结果小于3是无法组成满足斐波那契序列的,返回0即可。

代码分析:

1、dfs

首先看超时的dfs:

var result int
var dfs func(arr []int, start, l, num1, num2, num int)
dfs = func(arr []int, start, l, num1, num2, num int) {
    if result < num && num > 2  {
        result = num
    }
    if start >= l {
        return
    }
    for index := start; index < l; index++ {
        if num1+num2 == arr[index] || num1 == 0 || num2 == 0 {
            num++
            dfs(arr, index+1, l, num2, arr[index], num)
            num--
        }
    }
}

因为起始状态,num1num2并没有存任何的数,这里只需做num1 == 0 || num2 == 0条件的额外判断即可。

2、dp + hash

首先定义map以及初始化dp数组:

l := len(arr)
m := make(map[int]int)
dp := make([][]int, l)
for index := 0; index < l; index++ {
    m[arr[index]] = index
    dp[index] = make([]int, l)
}

dp的核心所在:

从二维数组的右下角开始遍历,如果满足条件,将其结果加1并且保存到最终结果中。

for row := l - 1; row >= 0; row-- {
    for col := l - 1; col > row; col-- {
        if index, ok := m[arr[col]+arr[row]]; ok {
            dp[row][col] = dp[col][index] + 1
            ans = max(ans, dp[row][col])
        }
    }
}

最后就是结果的额外处理:

if ans == 0 {
    return
}
return ans + 2

复杂度:

  • 时间复杂度:O(n ^ 2),动态规划所需时间
  • 空间复杂度:O(n ^ 2),动态规划数组所需空间

执行结果:

  • 执行用时: 192 ms , 在所有 Go 提交中击败了 55.84% 的用户
  • 内存消耗: 17.3 MB , 在所有 Go 提交中击败了 63.64% 的用户

通过次数 56.6K 提交次数 101.1K 通过率 56.0%

Related Posts

1026. 节点与其祖先之间的最大差值

## 题目描述:给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)示例 1: ![](/img/leetcode/1026节点与其祖先之间的最大差值/tmp-tre

read more

1038. 从二叉搜索树到更大和树

## 题目描述:给定一个二叉搜索树 root (BST),请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。提醒一下, 二叉搜索树 满足下列约束条件:- 节点的左子树仅包含键 小于 节点键的节点。 - 节点的右子树仅包含键 大于 节点键的节点。 - 左右子树也必须是二叉搜索树。*示例 1:***![](/img/leetcode/

read more

105. 从前序与中序遍历序列构造二叉树

## 题目描述:给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。示例 1: ``` 输入: preorder = [3,9,20,15,7], inorder = [

read more

106. 从中序与后序遍历序列构造二叉树

## 题目描述:给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。示例 1: ``` 输入:inorder = [9,3,15,20,7], postorder

read more

109. 有序链表转换二叉搜索树

## 题目描述:给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。示例 1: ``` 输入: head = [-10,-3,0,5,9] 输出: [0,-3,9

read more

114. 二叉树展开为链表

## 题目描述:给你二叉树的根结点 root ,请你将它展开为一个单链表:- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。示例 1: ``` 输入:root

read more