子序列宽度之和

166 阅读2分钟

子序列宽度之和

题目

原题链接:子序列宽度之和

一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大,请返回对 10^9 + 7 取余 后的结果。 子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。  

示例 1:

输入: nums = [2,1,3]
输出: 6
解释: 子序列为 [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3] 。
相应的宽度是 0, 0, 0, 1, 1, 2, 2 。
宽度之和是 6 。

示例 2:

输入: nums = [2]
输出: 0

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

题解

暴力(时间超限)

思路:暴力的想法就是先枚举出该数组的全部序列,然后求出每个序列的最大值和最小值,最后求和取余,显然,该枚举过程是非常超时的,因为数组的长度是10^5,所以该方法不可取。

class Solution {

    List<Integer> path = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<>();
    public int sumSubseqWidths(int[] nums) {
        dfs(0, nums);
        long res = 0;
        for (List<Integer> list: ans) {
            //求最大值和最小值
            int min = list.get(0);
            int max = list.get(0);
            for (int x: list) {
                if (max < x) {
                    max = x;
                }
                if (min > x) {
                    min = x;
                }
            }
            //求和并取余
            res += max - min;
            res %= 1e9 + 7;
        }
        return (int)res;
    }
    //枚举所有子序列
    public void dfs(int index, int[]nums) {
        if (index >= nums.length) {
            if (path.size() > 0) {
                ans.add(new ArrayList<>(path));
            }
           
            return;
        }
        //取或者不取
        path.add(nums[index]);
        dfs(index + 1, nums);
        path.remove(path.size() - 1);
        dfs(index + 1, nums);
    }
}

排序 + 数学

思路来源:传送门

我们先对数组进行排序,因为是子序列,所以排序并不影响最终结果,我们计算每个元素对最终答案的贡献值。对于nums数组中的nums[i],其要么是最大值要么是最小值:

  • 如果nums[i]是最大值,那么对于nums[0]、nums[1]....nums[i - 1],一共有2^i种答案,因为其能够组成的子序列共有2^i种,那么作为最大值的贡献值为nums[i]*2^i
  • 如果nums[i]是最小值,那么对于nums[i + 1]、nums[i + 2]...nums[n - 1],一共有2^(n-i-1)种答案,原因同上,其作为最小值的贡献值为nums[i]*2^(n-i-1)
  • 所以nums[i]的最终贡献值就等于nums[i]*(2^i-2^(n-i-1))
class Solution {

    public int sumSubseqWidths(int[] nums) {
        //排序
        Arrays.sort(nums);
        long mod = 1000000007;
        long res = 0;
        int len = nums.length;
        long[] p = new long[len];
        p[0] = 1;
        //取模运算
        for (int i = 1; i < len; i++) {
            p[i] = p[i - 1] * 2 % mod;
        }
        //贡献值 = nums[i]*(2^i-2^(n-i-1))
        for (int i = 0; i < len; i++) {
            res = (res + nums[i] * (p[i] - p[len - i - 1])) % mod;
        }
        return (int)res;
    }
}