[LeetCode] 1027. Longest Arithmetic Subsequence 最长的等差数列
Given an array A
of integers, return the length of the longest arithmetic subsequence in A
.
Recall that a subsequence of A
is a list A[i_1], A[i_2], ..., A[i_k]
with 0 <= i_1 < i_2 < ... < i_k <= A.length - 1
, and that a sequence B
is arithmetic if B[i+1] - B[i]
are all the same value (for 0 <= i < B.length - 1
).
Example 1:
Input: A = [3,6,9,12]
Output: 4
Explanation:
The whole array is an arithmetic sequence with steps of length = 3.
Example 2:
Input: A = [9,4,7,2,10]
Output: 3
Explanation:
The longest arithmetic subsequence is [4,7,10].
Example 3:
Input: A = [20,1,15,3,10,5,8]
Output: 4
Explanation:
The longest arithmetic subsequence is [20,15,10,5].
Constraints:
2 <= A.length <= 1000
0 <= A[i] <= 500
这道题给了一个数组,让找最长的等差数列的长度,暴力搜索的时间复杂度太大,这里就不考虑了。像这种玩数组,求极值的题,刷题老司机们应该很容易就反应出应该用动态规划 Dynamic Programming 来做,首先来考虑如何定义 DP 数组,最直接的就是用一个一维数组,其中 dp[i] 表示区间 [0, i] 中的最长等差数列的长度,但是这样定义的话,很难找出状态转移方程。因为有些隐藏信息被我们忽略了,就是等差数列的相等的差值,不同的等差数列的差值可以是不同的,所以不包括这个信息的话将很难更新 dp 值。所以这里就需要一个二维数组,dp[i][j] 表示在区间 [0, i] 中的差值为j的最长等差数列的长度减1,这里减1是因为起始的数字并没有被算进去,不过不要紧,最后再加回来就行了。还有一个需要注意的地方,由于等差数列的差值有可能是负数,而数组的下标不能是负数,所以需要处理一下,题目中限定了数组中的数字范围为0到 500 之间,所以差值的范围就是 -500 到 500 之间,可以给差值加上个 1000,这样差值范围就是 500 到 1500 了,二维 dp 数组的大小可以初始化为 nx2000
。更新 dp 值的时候,先遍历一遍数组,对于每个遍历到的数字,再遍历一遍前面的所有数字,算出差值 diff,再加上 1000,然后此时的 dp[i][diff] 可以赋值为 dp[j][diff]+1,然后用这个新的 dp 值来更新结果 res,最后别忘了 res 加1后返回,参见代码如下:
class Solution {
public:
int longestArithSeqLength(vector<int>& A) {
int res = 0, n = A.size();
vector<vector<int>> dp(n, vector<int>(2000));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
int diff = A[i] - A[j] + 1000;
dp[i][diff] = dp[j][diff] + 1;
res = max(res, dp[i][diff]);
}
}
return res + 1;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1027
参考资料:
https://leetcode.com/problems/longest-arithmetic-subsequence/
https://leetcode.com/problems/longest-arithmetic-subsequence/discuss/274611/JavaC%2B%2BPython-DP