从N个整数数组中选择有序三胞胎的不同方法

时间:2018-07-30 18:31:30

标签: algorithm permutation dynamic-programming

给定一个由n个整数组成的数组A,我想找到选择有序三胞胎的方法。例如。

A = [1, 2, 1, 1]
different ways are  (1, 2, 1), (1, 1, 1) and (2, 1, 1)
so the answer will be 3.

for A = [2, 2, 1, 2, 2]
different ways are (1, 2, 2), (2, 1, 2), (2, 2, 1) and (2, 2, 2)
so the answer will be 4 in this case

如果所有数字都是唯一的,那么我想出了一个循环

f(n) = f(n-1) + ((n-1) * (n-2))/2
where f(3) = 1 and f(2) = f(1) = 0

重复数字时遇到麻烦。这需要在O(n)时间和O(n)空间中解决。

1 个答案:

答案 0 :(得分:1)

对于大小为 idx 的数组,唯一,有序集的数量的动态编程关系为:

DP [组的大小] [idx] = DP [组的大小] [idx-1] + DP [组的大小-1] [idx-1]-DP [组的大小-1] [last_idx [ A [idx]-1]

因此,要从 idx 元素数组计算大小为LEN的有序唯一集合的数量:

  • 计算可从 idx -1元素数组创建的大小为LEN的有序唯一集合的数量
  • 添加大小为LEN-1的有序唯一集的末尾可以添加元素 idx 形成的有序唯一集的数量
  • 不要重复计算。通过将元素 idx 的上次出现添加到大小为LEN-1的有序唯一集的末尾,减去可以形成的有序唯一集的数量。

之所以有用,是因为在遍历数组时,我们总是在计数唯一集合。对唯一集合进行计数是基于先前唯一集合的元素计数。

因此,从大小为1的组开始,然后再做大小2,然后是大小3,依此类推。

对于恒定大小LEN的唯一,有序集,我的函数占用O(LEN * N)内存和O(LEN * N)时间。您应该能够重用DP阵列以将内存减少到一个与LEN,O(constant * N)无关的常数。

这是功能。

    static int answer(int[] A) {
    // This example is for 0 <= A[i] <= 9.  For an array of arbitrary integers, use a proper
    //   HashMap instead of an array as a HashMap. Alternatively, one could compress the input array
    //   down to distinct, consecutive numbers. Either way max memory of the last_idx array is O(n).
    //   This is left as an exercise to the reader.
    final int MAX_INT_DIGIT = 10;
    final int SUBSEQUENCE_LENGTH = 3;
    int n = A.length;

    int[][] dp = new int[SUBSEQUENCE_LENGTH][n];
    int[] last_idx = new int[MAX_INT_DIGIT];
    Arrays.fill(last_idx, -1);

    // Init dp[0] which gives the number of distinct sets of length 1 ending at index i
    dp[0][0] = 1;
    last_idx[A[0]] = 0;

    for (int i = 1; i < n; i++) {
        if (last_idx[A[i]] == -1) {
            dp[0][i] = dp[0][i - 1] + 1;
        } else {
            dp[0][i] = dp[0][i - 1];
        }
        last_idx[A[i]] = i;
    }

    for (int ss_len = 1; ss_len < SUBSEQUENCE_LENGTH; ss_len++) {
        Arrays.fill(last_idx, -1);
        last_idx[A[0]] = 0;
        for (int i = 1; i < n; i++) {
            if (last_idx[A[i]] <= 0) {
                dp[ss_len][i] = dp[ss_len][i - 1] + dp[ss_len-1][i - 1];
            } else {
                dp[ss_len][i] = dp[ss_len][i - 1] + dp[ss_len-1][i - 1] - dp[ss_len-1][last_idx[A[i]] - 1];
            }
            last_idx[A[i]] = (i);
        }
    }

    return dp[SUBSEQUENCE_LENGTH-1][n - 1];
}

对于[3 1 1 3 8 0 5 8 9 0],我得到的答案是62。