题目描述
给定一个整数数组 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(n^2)$
- 设状态$f(i, j)$ 表示以 $i$ 结尾的子序列,差为 $j$ 时的最长子序列长度。
- 初始时,全部为 0。
- 转移时,对于当前位置 $i$,从 0 开始循环枚举 $0 \le j < i$,计算
diff = A[i] - A[j]
,然后转移 $f(i, diff) = f(j, diff) + 1$。 - 答案为 $f$ 数组中的最大值加 1。
时间复杂度
- 状态数为为 $O(n)$ 个,每次转移需要 $O(n)$ 的时间,故总时间复杂度为 $O(n^2)$。
空间复杂度
- 和时间复杂度类似,总空间复杂度为 $O(n^2)$。
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++不会超时
不用hash,数组从前到后,从后到前两遍过了
int dp[n][d + 1];
这么定义数组是不符合 C++ 规范的,在开启编译优化时会出现未定义的情况定义动态数组一般要用指针,或者用 vector 容器构造
leetcode加强了数据,TLE了现在
改成了 Go