跳到主要内容

891.子序列宽度之和

· 阅读需 3 分钟

1、题干

一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。

给你一个整数数组 nums ,返回 nums 的所有非空 子序列宽度之和 。由于答案可能非常大,请返回对 109 + 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 <= 105
  • 1 <= nums[i] <= 105

Problem: 891. 子序列宽度之和

2、思路

还得是按官解的排序+计算贡献值的思路去做:

  • 对长度为 n 的整数数组 nums 升序排列
  • 数组 nums 中第 i 个整数贡献的最小值次数是: 2ni112 ^ {n-i-1} - 1
  • 数组 nums 中第 i 个整数贡献的最大值次数是: 2i12 ^ i - 1
  • 累计每个元素的贡献值 nums[i](2i1)nums[i](2ni11)nums[i] * (2 ^ i - 1) - nums[i] * (2 ^ {n-i-1} - 1)

计算贡献次数实际是计算组合数:Cn1+Cn2+...+CnnC_{n}^{1} + C_{n}^{2} + ... + C_{n}^{n}

由于数据过大需要取余,第一版用快速幂实现,居然只通过了 20+ 个用例,真是裂开了😳;把快速幂改成打表后过了。。。

3、Code

function sumSubseqWidths(nums: number[]): number {
const n = nums.length, MOD = 1e9 + 7;
nums.sort((a, b) => a - b);
const pow = new Array(n).fill(1);
for (let i = 1; i < n; i++) pow[i] = 2 * pow[i - 1] % MOD;

let res = 0;
for (let i = 0; i < n; i++) {
res = (res + nums[i] * (pow[i] - pow[n - i - 1])) % MOD;
}

return res;
};

4、复杂度

  • 时间复杂度:O(nlogn)O(n*logn)
  • 空间复杂度:O(n)O(n)

5、执行结果

image.png