【问题标题】:No. of distinct subsequences of length 3 in an array of length n长度为 n 的数组中长度为 3 的不同子序列的数量
【发布时间】:2018-11-15 02:20:58
【问题描述】:

如何计算长度为n的数组中长度为3(或一般长度为k < n)的不同子序列的数量?

注意:如果两个子序列中的元素顺序不同,则认为它们不同。

例如:假设数组A = [1, 2, 1, 1],那么答案应该是3,因为只有三个不同的长度为3的子序列如下所示:

[1, 1, 1]
[1, 2, 1]
[2, 1, 1]

数组n <= 10^5的大小,数组中的每个元素A_i <= n

我的做法:

我想出了蛮力方法,即获取长度为 3 的元组并将其插入到地图中。但这既不是空间/时间效率的。

编辑:这是一个面试问题,它说对于k = 3,预期的时间和空间复杂度是O(n)

【问题讨论】:

  • 您不应该将 1、1、2 作为解决方案的一部分吗?
  • @kishore 根据定义,数组子序列的元素需要以与原始数组相同的顺序出现。因此,我们不能拥有1, 1, 2

标签: arrays algorithm math dynamic-programming counting


【解决方案1】:

与面试问题的常见情况一样,有一个动态编程解决方案。让T(m, k) 是第一个m 元素的不同长度-k 子序列的数量。然后假设对输入 A 进行基于 1 的索引,我们有一个 2D 递归

T(m, 0) = 1
T(m, k) = T(m-1, k) + T(m-1, k-1) -
          ^^^^^^^^^   ^^^^^^^^^^^
     A_m not chosen   A_m chosen

            { T(i-1, k-1), if i < m is the maximum index where A_i = A_m
            { 0,           if no such index exists

减去的项确保我们不计算重复项;更多解释见https://stackoverflow.com/a/5152203/2144669

运行时间(使用哈希映射来维护到目前为止看到的每个符号的最右边出现)是O(k n),对于k = 3,它是O(n)

【讨论】:

    【解决方案2】:

    这里有一个稍微不同的看法。我们可以认为元素m在子序列中可以是kth的方式数是任何元素(包括m)之前出现的所有方式的总和可以是(k-1)th。然而,当我们向右移动时,唯一需要更新的是m;其他总和保持不变。

    例如,

    // We want to avoid counting [1,1,1], [1,2,1], etc. twice
    [1, 2, 1, 1, 1]
    

    (为方便起见垂直显示数组)

                <-  k  ->
    [1,  ->  1: [1, 0, 0]
     2,  ->  2: [1, 1, 0]
     1,  ->  1: [1, 2, 1]
     1,  ->  1: [1, 2, 3]
     1]  ->  1: [1, 2, 3]
    

    现在如果我们添加另一个元素,比如 3,

    ...
     3]  ->  3: [1, 2, 3]
    
     // 1 means there is one way
     // the element, 3, can be first
    
     // 2 means there are 2 ways
     // 3 can be second: sum distinct
     // column k[0] = 1 + 1 = 2
    
     // 3 means there are 3 ways
     // 3 can be third: sum distinct
     // column k[1] = 2 + 1 = 3
    

    汇总不同的k[2] 列:

    0 + 3 + 3 = 6 subsequences
    
    [1,2,1], [2,1,1], [1,1,1]
    [1,1,3], [2,1,3], [3,2,1]
    

    每列的 sum-distinct 可以在每次迭代时在 O(1) 中更新。当前元素的 k 总和(我们为每个元素更新一个列表),取 O(k),在我们的例子中是 O(1)

    JavaScript 代码:

    function f(A, k){
      A.unshift(null);
      
      let sumDistinct = new Array(k + 1).fill(0);
      let hash = {};
    
      sumDistinct[0] = 1;
    
      for (let i=1; i<A.length; i++){
        let newElement;
        
        if (!hash[A[i]]){
          hash[A[i]] = new Array(k + 1).fill(0);
          newElement = true;
        }
        
        let prev = hash[A[i]].slice();
    
        // The number of ways an element, m, can be k'th
        // in the subsequence is the sum of all the ways
        // the previous occurence of any element
        // (including m) can be (k-1)'th
        for (let j=1; j<=k && j<=i; j++)
          hash[A[i]][j] = sumDistinct[j - 1];
    
        for (let j=2; j<=k && j<=i; j++)
          sumDistinct[j] = sumDistinct[j] - prev[j] + hash[A[i]][j];
    
        if (newElement)
          sumDistinct[1] += 1;
    
        console.log(JSON.stringify([A[i], hash[A[i]], sumDistinct]))
      }
    
      return sumDistinct[k];
    }
    
    var arr = [1, 2, 1, 1, 1, 3, 2, 1];
    
    console.log(f(arr, 3));

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-13
      • 1970-01-01
      • 1970-01-01
      • 2022-11-26
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 2019-07-13
      相关资源
      最近更新 更多