长度为n

时间:2018-06-05 14:36:09

标签: arrays algorithm math dynamic-programming counting

如何计算长度为3的数组中长度为k < n(通常为长度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)

2 个答案:

答案 0 :(得分:9)

与面试问题一样,有动态编程解决方案。设T(m, k)为第一个k元素的不同长度 - m子序列的数量。然后在输入A上假设基于一个索引,我们有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)O(n)k = 3

答案 1 :(得分:3)

这是一个略有不同的看法。我们可以想到元素m在子序列中可以k的方式的数量,作为任何元素(包括m)之前出现的所有方式的总和是(k-1)。然而,当我们向右移动时,唯一需要的更新是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));