A sequence X1, X2, …, Xn is Fibonacci-like if:
n >= 3
Xi + Xi+1 = Xi+2 for all i + 2 <= n
Given a strictly increasing array arr of positive integers forming a sequence, return the length of the longest Fibonacci-like subsequence of arr. 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].
Constraints:
3 <= arr.length <= 1000
1 <= arr[i] < arr[i + 1] <= 109
求最长的Fibonacci子序列的长度。
Fibonacci子序列是指Xi + Xi+1 = Xi+2 for all i + 2 <= n
思路:
Xi + Xi+1 = Xi+2
知道Xi+2时,还需要知道Xi和Xi+1,也就是说,到Xi+2为止的数组,它的Fb子序列的长度,等于以Xi和Xi+1为结尾的Fb子序列的长度+1。
假如arr[i], arr[j], arr[k]可组成Fb子序列,定义dp[i][j]为 以arr[i]和arr[j]结尾的Fb子序列长度,那么dp[j][k] = dp[i][j] + 1,也就是说,以arr[j], arr[k]为结尾的Fb子序列长度,是在arr[i], arr[j]为结尾的Fb序列长度基础上加1的。
可不断遍历j,在j的右边遍历k,然后找到是否存在i,使arr[i]+arr[j] == arr[k],如果找到,dp[j][k] = dp[i][j]+1
至于如何找arr[i],可事先把(arr[i], i)存入HashMap,在HashMap中找arr[k]-arr[j]即可。
序列是严格递增的,所以arr[i] < arr[j],如果arr[k] - arr[j],也就是arr[i] >= arr[j]时,可剪枝直接跳到下一个j。
最短的Fb子序列长度是3,所以要初始化长度为2,后面在此基础上不断+1。或者初始化为0,在结果上统一+2
public int lenLongestFibSubseq(int[] arr) {
HashMap<Integer, Integer> map = new HashMap<>();
int n = arr.length;
int[][] dp = new int[n][n];
int result = 0;
for(int i = 0; i < n; i++) {
map.put(arr[i], i);
}
for(int j = 1; j < n-1; j++) {
for(int k = j+1; k < n; k++) {
int ai = arr[k] - arr[j];
if(ai >= arr[j]) break;
if(map.containsKey(ai)) {
dp[j][k] = dp[map.get(ai)][j] + 1;
result = Math.max(result, dp[j][k]);
}
}
}
return result==0 ? 0 : result+2;
}
既然序列是严格递增的,已知arr[k],可用双指针来找arr[i]和arr[j],使arr[i]+arr[j]=arr[k],dp方法和上面一样。
public int lenLongestFibSubseq(int[] arr) {
int n = arr.length;
int[][] dp = new int[n][n];
int result = 0;
for(int i = 2; i < n; i++) {
int left = 0;
int right = i - 1;
while(left < right) {
int tmpSum = arr[left] + arr[right];
if(tmpSum > arr[i]) {
right --;
} else if(tmpSum < arr[i]) {
left ++;
} else {
dp[right][i] = dp[left][right] + 1;
result = Math.max(result, dp[right][i]);
left ++;
right --;
}
}
}
return result==0 ? 0 : result+2;
}