leetcode每日一题系列-等差数列划分II-子序列

·  阅读 157
leetcode每日一题系列-等差数列划分II-子序列

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

leetcode-416-等差数列划分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
  • 231<=nums[i]<=2311-2^{31} <= nums[i] <= 2^{31} - 1

Related Topics

  • 数组
  • 动态规划

👍 157 👎 0

[题目链接]

leetcode题目链接

[github地址]

代码链接

[思路介绍]

思路一:暴力法

  • 我幻想了一种解法,三个指针,求出所有等差子序列
  • 然后去重
  • 去重失败
  • 哭了

思路二:序列动态规划

  • 按照三叶大佬的思路来看
  • 我们可以先简单的使用 一维方程f[i] 来表示使用 nums[i] 为等差子序列终点的数量
  • 然后观察是否满足dp的严谨性,如果不严谨,则增加其他维度
  • 验证严谨性:
    • f[i]按照当前状态来定义
    • dp移动的时候可以很容易的得出等差子序列的公差为nums[i]-nums[j]
    • 可以发现这个时候公差是固定的值
    • 而本题要求的是所有等差子序列
    • 而不是固定公差的等差子序列
  • 所以增加维度f[i][j]
  • 表示以j为公差,nums[i] 为结尾的等差子序列的数量
  • 求f[i][j]的移动方程
  • 对于所有0->i区间内节点j
  • 公差d = nums[i] - nums[j]
  • 我们可以想到f[i][d] = j=0i1f[j][d]+1\sum_{j=0}^{i-1}f[j][d] + 1
  • 可以表示为 j 从0开始到i-1结束
  • 所有与j为等差子数列,且公差为d的数量的和 再加1(加上当前nums[i])
  • 感谢benhao大佬举的例子让我豁然开朗
    • [2, 4, 6, 8],到6这里,公差为2的序列有俩
    • [2,4,6]和[4,6]
    • 那么到8这里
    • 就有[2,4,6,8],[4,6,8]和[6,8]
    • 6可以很很多组2,4构成序列
    • 但是多的只有[6,8]
  • 从上述例子也可以看出是没有考虑子数组长度小于3的情况
  • 长度为 2 的等差子序列,由于没有第三个数的差值限制
  • 因此任意的数对 (j, i) 都是一个合法的长度为 2 的等差子序列。
  • 而求长度为 n 的数组的所有数对,其实就是求 首项为 0,末项为 n - 1,公差为 1,长度为 n 的等差数列之和,
  • 直接使用等差数列求和公式求解即可。
public int numberOfArithmeticSlices(int[] nums) {
            List<Map<Long, Integer>> list = new ArrayList<>();
            for (int i = 0; i < nums.length; i++) {
                Map<Long, Integer> map = new HashMap<>();
                for (int j = 0; j < i; j++) {
                    long dis = nums[i]*1L- nums [j];
                    Map<Long, Integer> prev = list.get(j);
                    int cnt = map.getOrDefault(dis,0);
                    cnt += prev.getOrDefault(dis,0);
                    cnt++;
                    map.put(dis,cnt);
                }
                list.add(map);
            }
            int ans = 0;
            for (int i = 0; i < nums.length; i++) {
                Map<Long, Integer> cur = list.get(i);
                for (Long key : cur.keySet()) {
                    ans += cur.get(key);
                }
            }
            int a1 = 0, an = nums.length - 1;
            int cnt = (a1 + an) * nums.length / 2;
            return ans - cnt;

        }
复制代码
  • 时间复杂度O(n2n^2)
  • 空间复杂度O(n2n^2)
分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改