LeetCode每日一题 446. 等差数列划分 II – 子序列

这是我参与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,那么下标为ik的两个数就可以构成长度等于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
喜欢就支持一下吧
点赞0 分享