给了一个数组,求等差数列有多少个,要求等差数列的长度至少为3. 比如:
Input: [2, 4, 6, 8, 10] Output: 7 Explanation: All arithmetic subsequence slices are: [2,4,6] [4,6,8] [6,8,10] [2,4,6,8] [4,6,8,10] [2,4,6,8,10] [2,6,10]
数组长度N <= 1000, 数组元素的值在int范围内
这个题目显然是个dp题目,如何定义状态呢?
我们定义dp[i][diff] 表示为以nums[i]结尾的,公差为diff的等差数列的长度的个数. 这里的等差数列的长度至少为2
因此我们有如下转移方程:
dp[i][diff] = dp[j][diff] + 1, 其中j < i
什么意思呢?
以nums[i]结尾的公差为diff的等差数列 (长度>=2),可以由两种方式得到
1: 直接和num[j]组合为 (nums[j], nums[i]),这样组成的长度为2
2:以nums[j]结尾的公差为d的等差数列,也就是dp[j][diff],后面跟上nums[i]得到,这样组成的长度 >=3.
由以上分析,可以在得到dp[i][diff]的同时,可以把第二种情况累加到答案中去
于是我写出了这样的代码:
public int numberOfArithmeticSlices(int[] A) {
int len = A.length;
int[][] dp = new int[len][1000010];
//dp[i][d] 以 num[i]结尾的差为d等差数列的个数,这个等差数列长度至少为2
int res = 0;
for (int i = 0; i < len; i++) {
for (int j = 0; j < i; j++) {
//求出公差
int diff = A[i] - A[j];
//以nums[i]结尾的公差为diff的长度为>=2的等差数列可以由两种方式得来
//1: (nums[j], nums[i])可以构成一个公差为diff的长度为2的等差数列
//2: dp[j][diff]表示以nums[j]公差为diff的长度为2的等差数列,我们在其后面加上一个nums[i],便可以构成长度>=3的等差数列
// 我们可以把这部分累加到最终的答案里,因为题目要求等差数列的长度至少为3
dp[i][diff] += 1;
dp[i][diff] += dp[j][diff];
res += dp[j][diff];
}
}
return res;
}
提交上去,显然过不了 如下的数据
0,2000000000,-294967296
因为数据是在int范围内的,因此第二维的diff空间肯定是存不下的,怎么办呢?使用一个map就可以解决这个问题了
public int numberOfArithmeticSlices(int[] A) {
int len = A.length;
Map<Long, Integer>[] dp = new Map[len];
//dp[i].get(d) 以 num[i]结尾的差为d等差数列的个数,这个等差数列长度至少为2
int res = 0;
for (int i = 0; i < len; i++) {
dp[i] = new HashMap<>(i);
for (int j = 0; j < i; j++) {
//求出公差
long diff = (long)A[i] - A[j];
if (diff <= Integer.MIN_VALUE || diff >= Integer.MAX_VALUE) continue;
//以nums[i]结尾的公差为diff的长度为>=2的等差数列可以由两种方式得来
//1: (nums[j], nums[i])可以构成一个公差为diff的长度为2的等差数列
//2: dp[j].get(diff) 表示以nums[j]公差为diff的长度为2的等差数列,我们在其后面加上一个nums[i],便可以构成长度>=3的等差数列
// 我们可以把这部分累加到最终的答案里,因为题目要求等差数列的长度至少为3
int v = dp[i].getOrDefault(diff, 0);
int add = dp[j].getOrDefault(diff, 0);
v += add + 1;
res += add;
dp[i].put(diff, v);
}
}
return res;
}