873. 最长的斐波那契子序列的长度
873. 最长的斐波那契子序列的长度
题目
A sequence x1, x2, ..., xn
is Fibonacci-like if:
n >= 3
xi + xi+1 == xi+2
for alli + 2 <= n
Given a strictly increasing array arr
of positive integers forming a sequence, return the length of the longest Fibonacci-like subsequence ofarr
. If one does not exist, return 0
.
A subsequence is derived from another sequence arr
by deleting any number of elements (including none) from arr
, without changing the order of the remaining elements. For example, [3, 5, 8]
is a subsequence of [3, 4, 5, 6, 7, 8]
.
Example 1:
Input: arr = [1,2,3,4,5,6,7,8]
Output: 5
Explanation: The longest subsequence that is fibonacci-like: [1,2,3,5,8].
Example 2:
Input: arr = [1,3,7,11,12,14,18]
Output: 3
Explanation : The longest subsequence that is fibonacci-like: [1,11,12], [3,11,14] or [7,11,18].
Constraints:
3 <= arr.length <= 1000
1 <= arr[i] < arr[i + 1] <= 10^9
题目大意
如果序列 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
解题思路
思路一:动态规划 + 双指针
状态定义 我们用
dp[cur][next]
表示以arr[cur]
和arr[next]
作为最后两个元素的最长斐波那契式子序列的长度。状态转移 我们遍历
next
作为子序列的最后一个元素,使用prev
和cur
作为前两个元素:
- 如果
arr[prev] + arr[cur] > arr[next]
,说明当前prev
太大了,cur--
(向前调整cur
)。 - 如果
arr[prev] + arr[cur] < arr[next]
,说明prev
太小了,prev++
(向后调整prev
)。 - 如果
arr[prev] + arr[cur] == arr[next]
,说明arr[prev]
、arr[cur]
、arr[next]
可以组成斐波那契式子序列:dp[cur][next] = dp[prev][cur] + 1
- 更新最长子序列的长度
maxLen
- 继续向前寻找新的
prev
和cur
组合。
- 初始状态
dp[cur][next]
初始值为 0,表示默认没有匹配的斐波那契式子序列。maxLen
记录最长的序列长度,初始化为 0。
- 终止条件
- 遍历完整个数组后,如果
maxLen > 0
,说明存在符合要求的序列,返回maxLen + 2
(因为dp[cur][next]
记录的是额外的长度,需要加上prev
和cur
)。 - 否则返回
0
(表示不存在符合要求的斐波那契子序列)。
复杂度分析
- 时间复杂度:
O(n^2)
,遍历所有可能的(prev, cur, next)
组合。 - 空间复杂度:
O(n^2)
,使用dp
数组存储状态。
思路二:暴力遍历
枚举所有起点 我们尝试所有
arr[i]
和arr[j]
作为子序列的前两个元素(prev, cur)
,然后查找后续元素是否满足斐波那契关系。查找后续元素
- 计算
next = prev + cur
,判断next
是否在arr
中:- 如果
next
存在,则更新prev = cur
,cur = next
,长度len++
,继续查找。 - 如果
next
不存在,则停止,记录maxLen
。
- 如果
- 终止条件
- 遍历完整个数组后,返回
maxLen
作为最长子序列的长度。
复杂度分析
- 时间复杂度:
O(n^2 logM)
,其中M
为arr
的最大元素,查找操作while(Set.has())
近似O(logM)
。 - 空间复杂度:
O(n)
,使用Set
存储arr
中的元素,加速查找。
代码
/**
* @param {number[]} arr
* @return {number}
*/
var lenLongestFibSubseq = function (arr) {
const n = arr.length;
let dp = new Array(n).fill().map(() => new Array(n).fill(0));
let maxLen = 0;
for (let next = 2; next < n; next++) {
let prev = 0;
let cur = next - 1;
while (prev < cur) {
let sum = arr[prev] + arr[cur];
if (sum > arr[next]) {
cur--;
} else if (sum < arr[next]) {
prev++;
} else {
dp[cur][next] = dp[prev][cur] + 1;
maxLen = Math.max(maxLen, dp[cur][next]);
cur--;
prev++;
}
}
}
return maxLen == 0 ? 0 : maxLen + 2;
};
/**
* @param {number[]} arr
* @return {number}
*/
var lenLongestFibSubseq = function (arr) {
const n = arr.length;
let numSet = new Set(arr);
let maxLen = 0;
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
let prev = arr[j];
let cur = arr[i] + arr[j];
let len = 2;
while (numSet.has(cur)) {
len++;
let temp = cur;
cur += prev;
prev = temp;
maxLen = Math.max(maxLen, len);
}
}
}
return maxLen;
};
相关题目
题号 | 标题 | 题解 | 标签 | 难度 | 力扣 |
---|---|---|---|---|---|
509 | 斐波那契数 | [✓] | 递归 记忆化搜索 数学 1+ | 🟢 | 🀄️ 🔗 |