这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
446. 等差数列划分 II - 子序列
给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。
如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该序列为等差序列。
- 例如,
[1, 3, 5, 7, 9]、[7, 7, 7, 7]和[3, -1, -5, -9]都是等差序列。 - 再例如,
[1, 1, 2, 5, 7]不是等差序列。
数组中的子序列是从数组中删除一些元素(也可能不删除)得到的一个序列。
- 例如,
[2,5,10]是[1,2,1,***2***,4,1,***5***,***10***]的一个子序列。
题目数据保证答案是一个 32-bit 整数。
示例 1:
输入:nums = [2,4,6,8,10]
输出:7
解释:所有的等差子序列为:
[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]
示例 2:
输入:nums = [7,7,7,7,7]
输出:16
解释:数组中的任意子序列都是等差子序列。
提示:
1 <= nums.length <= 1000-2^31 <= nums[i] <= 2^31 - 1
方法一
昨天在做每日一题的时候起初把413. 等差数列划分理解成了今天这题,害,真难
动态规划:
-
状态定义:
dp[i][j]表示,以下标为i结尾,且等差数列的差值为j,且数列长度大于等于2的方案,值表示为方案的数量 -
状态转移:枚举下标比
i小的数,设下标为k- 如果
nums[i] - nums[k] == j,那么下标为i,k的两个数就可以构成长度等于2且差值为j的数列;状态的定义是,数列长度大于等于2的方案,那么大于2的方案就是以k为结尾,且等差数列的差值为j,且数列长度大于等于2的方案,而这刚好对应我们的状态定义,所以dp[i][j] += dp[k][j] + 1; - 如果
nums[i] - nums[k] != j,那么跳过即可;
- 如果
一般来说,分析到这,用代码实现就行了;但是,注意这题的两个数的差值可能会很大,超过Integer范围,所以,我们不能用枚举j来求解;那么,我们逆向行之,我们来枚举k,也就是i前面的数,再去判断nums[i] - nums[k]的差值j有没有在dp[k][j]出现过;这里用哈希表数组来实现这状态定义的二维数组,Map<Long, Integer>[] dp = Map[n]
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int n = nums.length;
Map<Long, Integer>[] dp = new Map[n];
for (int i = 0; i < n; i ++ )
dp[i] = new HashMap<Long, Integer>();
int res = 0;
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < i; j ++ ) {
Long k = nums[i] * 1l - nums[j];
int t = 0;
if (dp[j].containsKey(k)) {
res += dp[j].get(k);
t = dp[j].get(k);
}
dp[i].put(k, dp[i].getOrDefault(k, 0) + t + 1);
}
}
return res;
}
}
时间复杂度: O(n*n)
空间复杂度: O(n*n)