题目描述
给定一个整数数组 A
,返回 A
中最长等差子序列的长度。
回想一下,A
的子序列是列表 A[i_1], A[i_2], ..., A[i_k]
其中 0 <= i_1 < i_2 < ... < i_k <= A.length - 1
。并且如果 B[i+1] - B[i] (0 <= i < B.length - 1)
的值都相同,那么序列 B
是等差的。
样例
输入:[3,6,9,12]
输出:4
解释:
整个数组是公差为 3 的等差数列。
输入:[9,4,7,2,10]
输出:3
解释:
最长的等差子序列是 [4,7,10]。
输入:[20,1,15,3,10,5,8]
输出:4
解释:
最长的等差子序列是 [20,15,10,5]。
注意
2 <= A.length <= 1000
0 <= A[i] <= 500
算法
(动态规划) O(n2)
- 设状态f(i,j) 表示以 i 结尾的子序列,差为 j 时的最长子序列长度。
- 初始时,全部为 0。
- 转移时,对于当前位置 i,从 0 开始循环枚举 0≤j<i,计算
diff = A[i] - A[j]
,然后转移 f(i,diff)=f(j,diff)+1。 - 答案为 f 数组中的最大值加 1。
时间复杂度
- 状态数为为 O(n) 个,每次转移需要 O(n) 的时间,故总时间复杂度为 O(n2)。
空间复杂度
- 和时间复杂度类似,总空间复杂度为 O(n2)。
Go代码 1
func longestArithSeqLength(A []int) int {
n := len(A)
f := make([]map[int]int, n)
var ans int
for i := 0; i < n; i++ {
f[i] = make(map[int]int)
for j := 0; j < i; j++ {
diff := A[i] - A[j]
f[i][diff] = f[j][diff] + 1
if ans < f[i][diff] {
ans = f[i][diff]
}
}
}
return ans + 1
}
Go代码 2
- 由于
A
的范围较小,所以可以考虑直接用数组带偏移量的方式,避免使用map
。
func longestArithSeqLength(A []int) int {
n := len(A)
f := make([][]int, n)
max, min := getMaxAndMin(A)
maxDiff := 2 * (max - min) + 1
var ans int
for i := 0; i < n; i++ {
f[i] = make([]int, maxDiff)
for j := 0; j < i; j++ {
diff := A[i] - A[j] + max - min
f[i][diff] = f[j][diff] + 1
if ans < f[i][diff] {
ans = f[i][diff]
}
}
}
return ans + 1
}
func getMaxAndMin(A []int) (max int, min int) {
max = A[0]
min = A[0]
for _, v := range A {
if max < v {
max = v
}
if min > v {
min = v
}
}
return
}
这个c++不会超时
class Solution { public: int longestArithSeqLength(vector<int>& nums) { int n=nums.size(),ans=2; vector<vector<int>> f(n,vector<int>(2000,0)); for (int i=0;i<n;i++){ for (int j=0;j<i;j++){ int diff=nums[i]-nums[j]+1000; f[i][diff]=2; f[i][diff]=max(f[i][diff],f[j][diff]+1); ans=max(ans,f[i][diff]); } } return ans; } };
不用hash,数组从前到后,从后到前两遍过了
class Solution { public: int longestArithSeqLength(vector<int>& A) { int n = A.size(), ans = 0; int minm = *min_element(A.begin(), A.end()); int maxm = *max_element(A.begin(), A.end()); int d = maxm - minm; int dp[n][d + 1]; memset(dp, 0, sizeof dp); int res = 0; for (int i = 1; i < n; i++) for (int j = 0; j < i; j++) { int s = A[i] - A[j]; if (s >= 0) { dp[i][s] = dp[j][s] ? dp[j][s] + 1 : 1; res = max(res, dp[i][s]); } } reverse(A.begin(), A.end()); memset(dp, 0, sizeof dp); for (int i = 1; i < n; i++) for (int j = 0; j < i; j++) { int s = A[i] - A[j]; if (s >= 0) { dp[i][s] = dp[j][s] ? dp[j][s] + 1 : 1; res = max(res, dp[i][s]); } } return res + 1; } };
int dp[n][d + 1];
这么定义数组是不符合 C++ 规范的,在开启编译优化时会出现未定义的情况定义动态数组一般要用指针,或者用 vector 容器构造
leetcode加强了数据,TLE了现在
改成了 Go