这是我参与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)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END