LeetCode 891 - Sum of Subsequence Widths


https://leetcode.com/problems/sum-of-subsequence-widths/
Given an array of integers A, consider all non-empty subsequences of A.
For any sequence S, let the width of S be the difference between the maximum and minimum element of S.
Return the sum of the widths of all subsequences of A. 
As the answer may be very large, return the answer modulo 10^9 + 7.

Example 1:
Input: [2,1,3]
Output: 6
Explanation:
Subsequences are [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3].
The corresponding widths are 0, 0, 0, 1, 1, 2, 2.
The sum of these widths is 6.
  • Time Complexity: O(N \log N), where N is the length of A.
  • Space Complexity: O(N), the space used by pow2. (We can improve this to O(1) space by calculating these powers on the fly.) 
Let's try to count the number of subsequences with minimum A[i] and maximum A[j].
Algorithm
We can sort the array as it doesn't change the answer. After sorting the array, this allows us to know that the number of subsequences with minimum A[i] and maximum A[j] is 2^{j-i-1}. Hence, the desired answer is:
\sum\limits_{j > i} (2^{j-i-1}) (A_j - A_i)
= \big( \sum\limits_{i = 0}^{n-2} \sum\limits_{j = i+1}^{n-1} (2^{j-i-1}) (A_j) \big) - \big( \sum\limits_{i = 0}^{n-2} \sum\limits_{j = i+1}^{n-1} (2^{j-i-1}) (A_i) \big)
= \big( (2^0 A_1 + 2^1 A_2 + 2^2 A_3 + \cdots) + (2^0 A_2 + 2^1 A_3 + \cdots) + (2^0 A_3 + 2^1 A_4 + \cdots) + \cdots \big) - \big( \sum\limits_{i = 0}^{n-2} (2^0 + 2^1 + \cdots + 2^{N-i-2}) (A_i) \big)
= \big( \sum\limits_{j = 1}^{n-1} (2^j - 1) A_j \big) - \big( \sum\limits_{i = 0}^{n-2} (2^{N-i-1} - 1) A_i \big)
= \sum\limits_{i = 0}^{n-1} \big(((2^i - 1) A_i) - ((2^{N-i-1} - 1) A_i)\big)
= \sum\limits_{i = 0}^{n-1} (2^i - 2^{N-i-1}) A_i

  public int sumSubseqWidths(int[] A) {
    int MOD = 1_000_000_007;
    int N = A.length;
    Arrays.sort(A);

    long[] pow2 = new long[N];
    pow2[0] = 1;
    for (int i = 1; i < N; ++i)
      pow2[i] = pow2[i - 1] * 2 % MOD;

    long ans = 0;
    for (int i = 0; i < N; ++i)
      ans = (ans + (pow2[i] - pow2[N - 1 - i]) * A[i]) % MOD;

    return (int) ans;

  }
https://leetcode.com/problems/sum-of-subsequence-widths/discuss/161267/C++Java1-line-Python-Sort-and-One-Pass?page=2
https://blog.csdn.net/XX_123_1_RJ/article/details/81913307
很显然,对这个序列排序是不影响结果的。求一个数列的子集,利用二进制的方法,大家一定不会陌生,因为每个元素就两个状态吗?取或者不取。这个题目也可以是二进制的思想,具体思路如下:
(1)先对原始序列排序(排序是不影响结果的哦,这个很好理解)
(2)现在单独分析一个排序后的元素A[i],那么这个元素在所有子集中,有几次是被作为最大值的?又有几次是被作为最小值的?
(3)继续分析,很显然A[i]被作为最大值的次数为 2^i - 1次(这里i从0开始),如何理解?0 ~ i 的所有子集共有2^(i+1)种,又因为每一位又两种状态,所以取最后一位被选中的有 2^i种,再减去只取这一位的1种,所以最后只有2^i - 1 。
(4)继续分析,很显然A[i]被作为最小的次数为 2^(n - i - 1) - 1次(这里i从0开始,n为数列的长度),倒过来分析,此时的i为最低位。

    def sumSubseqWidths(self, A):

        n, res, mod = len(A), 0, 10**9 + 7
        A.sort()
        pow2 = [1]
        for i in range(1, n):  # 先把2的次方求好
            pow2.append(pow2[-1] * 2 % mod)

        for i, x in enumerate(A):
            res += (pow2[i] - 1) * x  # x 被作为最大值得 次数 应该加上
            res -= (pow2[n-1-i] - 1) * x  # x 被作为最小值得 次数 应该减去
            res %= mod
        return res
https://zxi.mytechroad.com/blog/math/leetcode-891-sum-of-subsequence-widths/
Sort the array, for A[i]:
  • i numbers <= A[i]. A[i] is the upper bound of 2^i subsequences.
  • n – i – 1 numbers >= A[i]. A[i] is the lower bound of 2^(n – i – 1) subsequences.
  • A[i] contributes A[i] * 2^i – A[i] * 2^(n – i – 1) to the ans.
ans=i=0n1Ai2iAi2ni1=i=0n1(AiAni1)2i
  int sumSubseqWidths(vector<int>& A) {
    constexpr long kMod = 1e9 + 7;
    const int n = A.size();
    sort(begin(A), end(A));
    long ans = 0;
    long p = 1;
    for (int i = 0; i < n; ++i) {
      ans = (ans + (A[i] - A[n - i - 1]) * p) % kMod;
      p = (p << 1) % kMod;
    }
    return (ans + kMod) % kMod;
  }
https://leetcode.com/problems/sum-of-subsequence-widths/discuss/161267/C%2B%2BJava1-line-Python-Sort-and-One-Pass
The order in initial arrays doesn't matter,
my first intuition is to sort the array.
For A[i]:
There are i smaller numbers,
so there are 2 ^ i sequences in which A[i] is maximum.
we should do res += A[i] * (2 ^ i)
There are n - i - 1 bigger numbers,
so there are 2 ^ (n - i - 1) sequences in which A[i] is minimum.
we should do res -= A[i] * 2 ^ (n - i - 1)
Time Complexity:
O(NlogN)
    public int sumSubseqWidths(int[] A) {
        Arrays.sort(A);
        long c = 1, res = 0, mod = (long)1e9 + 7;
        for (int i = 0; i < A.length; ++i, c = (c << 1) % mod)
            res = (res + A[i] * c - A[A.length - i - 1] * c) % mod;
        return (int)((res + mod) % mod);

    }
Q. why do we plus mod before return?
A: in Cpp and Java, mod on negative number will still get a negative number.
https://www.cnblogs.com/seyjs/p/9509261.html
题目定义的子序列宽度是最大值和最小值的差,因此可以忽略中间值。首先对数组排序,对于数组中任意一个元素,都可以成为子序列中的最大值和最小值而存在。例如数组[1,2,3,4,5,6],对于元素3来说,由左边[1,2]组成的所有子序列都可以以3为最大值的,而右边[4,5,6]组成的所有子序列都可以以3为最小值。根据排列组合的原理:[1,2]可以组成的子序列个数为C(2,1) + C(2,2) ,而[4,5,6]可以组成的子序列个数为C(3,1) + C(3,2) + C(3,3) ,同样根据组合计算定律,C(n,1) + C(n,2) + ...... + C(n,n) = 2^n - 1。因为以3为最大值是做被减数,以3为最小值是做减数,因此以3作为最大/最小值的所有子序列的和宽度和就是:(2 ^ 2 - 1)* 3 - (2 ^ 3-1)*3 。同理,也可以求出其他元素的宽度和,并最终求出总宽度和。根据组合的对称性,C(n,m) = C(n,n-m),因此只需要遍历一半的数组长度即可
    def sumSubseqWidths(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        A.sort()
        res = 0
        l = 1
        r = pow(2, len(A) - 1) 
        for i in range(len(A)/2):

            res += l * A[i]
            res -= r * A[i]

            res += r * A[len(A)-i-1]
            res -= l * A[len(A) - i - 1]

            l *= 2
            r /= 2
        return res % (pow(10,9) + 7)
https://zhanghuimeng.github.io/post/leetcode-891-sum-of-subsequence-widths/
不如转而考虑每个元素会在多少个子序列里做最大值,在多少个子序列里做最小值
对于子序列而言,顺序不是很重要,所以首先可以把数组排序。[1](这真是一个珍贵的直觉。)这之后,对于元素A[i],有i个比它更小的元素,所以在2 ^ i个子序列中,它是最大的元素;有n - i - 1个比它更大的元素,所以在2 ^ (n - i - 1)个子序列中,它是最大的元素。所以,就A[i]而言,result += (2^i - 2^(n-i-1)) * A[i]。在实际求解过程中,为了避免重复求2的幂次,可以把上述代码按2的幂次进行拆分。[2]
之前我对重复元素的情况有些疑虑,但现在想来并不是必要的。在上述方法中,我们对每个子序列都恰好考虑了两遍:一遍是开头元素,一遍是结尾元素。即使子序列中存在重复的元素,也并不会影响其中的最大值和最小值的数值。
https://blog.csdn.net/yanglingwell/article/details/81836848
先将原数组排序(排序并不会影响结果), 则,起始位置是 i, 结束位置是 j 的串的宽度一定是 A[j]-A[i],中间可以有串,也可以没有串。代码如下:

  for(int i = 0; i < A.size(); ++i)
  {
      for(int j = i+1; j < A.size(); ++j)
      {
         ans = (ans + (long long int)pow2[j-i-1]*(A[j]-A[i]))%1000000007;
      }
  }
复杂度 O(n^2), 会超时, 可以将中间的循环化简:



Counting sort
  int sumSubseqWidths(vector<int>& A) {
    constexpr long kMod = 1e9 + 7;
    const int n = A.size();    
    countingSort(A);
    long ans = 0;
    long p = 1;
    for (int i = 0; i < n; ++i) {
      ans = (ans + (A[i] - A[n - i - 1]) * p) % kMod;
      p = (p << 1) % kMod;
    }
    return (ans + kMod) % kMod;
  }
private:
  void countingSort(vector<int>& A) {    
    int max_v = INT_MIN;
    int min_v = INT_MAX;
    for (int a : A) {      
      if (a > max_v) max_v = a;
      if (a < min_v) min_v = a;      
    }      
    vector<int> c(max_v - min_v + 1);
    for (int a : A)
      ++c[a - min_v];
    int i = 0;
    int j = 0;
    while (i < c.size()) {
      if (--c[i] >= 0)
        A[j++] = i + min_v;
      else
        ++i;
    }
  }
https://buptwc.com/2018/08/23/Leetcode-891-Sum-of-Subsequence-Widths/
  1. 首先我们需要明确,题目所求的widths和数组的本身的顺序没有关系,所以排序不会影响最后的结果,其次如果子串长度为1则widths为0,所以可以不用考虑
  2. OK,对于[1,2,3,4]这个数组,我们从第二个数开始分析:
    1. dp[1] = dp[0] + (2-1)*1 (对于nums[1] = 2)
    2. dp[2] = dp[1] + (3-1)*2 + (3-2)*1 (对于nums[2] = 3)
    3. dp[3] = dp[2] + (4-1)*4 + (4-2)*2 + (4-3)*1 (对于nums[3] = 4)
  3. 相信这么一写你肯定已经初步看出规律了,但是如果按照这个规律不做任何处理依次累加的话复杂度仍然是O(n^2),肯定是不过关的,我们这里再写一下通用的式子吧:
  4. 至此,递推式关系已出,显然dp[0] = 0, dp[1] = A[1]-A[0].
思路:
  1. 注意递推式中有一个2^(i+1),因为这个i可以达到20000,所以我们必须先对所有2的次方进行预处理,也就是mod 1e9+7!(把这个作为全局变量,不要来一组数据又重新算一次
rec,mod,s = [],10**9+7,1
for i in range(20000):
    rec.append(s)
    s *= 2
    s %= mod

class Solution(object):
    def sumSubseqWidths(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        if len(A) < 2: return 0
        A.sort()
        dp = [0] * len(A)
        dp[0],dp[1] = 0,A[1]-A[0]
        for i in range(2,len(dp)):
            dp[i] = (3*dp[i-1] - 2*dp[i-2] + (A[i]-A[i-1]) * (rec[i]-1)) % mod
        return dp[-1]

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts