给定序列中所有增加的子序列的数量?

时间:2011-01-08 21:56:48

标签: algorithm

您可能听说过有关找到longest increasing subsequence的众所周知的问题。最优算法具有O(n*log(n))复杂度。

我在考虑在给定序列中找到所有增加子序列的问题。我找到了一个问题的解决方案,我们需要find a number of increasing subsequences of length k,其复杂度O(n*k*log(n))(其中 n 是序列的长度)。

当然,这个算法可以用于我的问题,但是我认为解决方案的复杂性有O(n*k*log(n)*n) = O(n^2*k*log(n))。我认为,必须有更好的(我的意思是 - 更快)解决方案,但我还不知道。

如果您知道如何解决在最佳时间/复杂度中找到给定序列中所有增加的子序列的问题(在这种情况下,最优=优于O(n^2*k*log(n))),请告诉我关于那个。

最后:这个问题不是作业。在我的演讲中提到了一个问题,即增加的子序列越来越长,我开始考虑给定序列中所有增加子序列的一般概念。

6 个答案:

答案 0 :(得分:12)

我不知道这是否是最佳的 - 可能不是,但这是O(n^2)中的DP解决方案。

dp[i] = number of increasing subsequences with i as the last element

for i = 1 to n do
    dp[i] = 1
    for j = 1 to i - 1 do
        if input[j] < input[i] then
            dp[i] = dp[i] + dp[j] // we can just append input[i] to every subsequence ending with j

然后只需要汇总dp

中的所有条目

答案 1 :(得分:6)

您可以按如下方式计算O(n log n)时间内增加子序列的数量。

回想一下最长增长子序列长度的算法:

对于每个元素,计算先前元素中的先前元素,并将该元素添加到该长度。

如果使用像平衡二进制这样的数据结构计算前一个算法,那么该算法在O(n ^ 2)时间内运行,并在O(n log n)中运行(或者在整数的情况下甚至更好)搜索树(BST)(或类似van Emde Boas树的更高级的整数)。

要修改此算法以计算序列数,请在每个节点的BST中存储以该元素结尾的序列数。处理列表中的下一个元素时,只需搜索前一个元素,计算以小于当前正在处理的元素的元素结束的序列数(在O(log n)时间内),并将结果存储在BST以及当前元素。最后,对树中每个元素的结果求和,得到结果。

请注意,增加序列的数量可能非常大,因此算术不再需要每次操作O(1)次。这需要加以考虑。

伪码:

ret = 0
T = empty_augmented_bst() // with an integer field in addition to the key
for x int X:

  // sum of auxiliary fields of keys less than x
  // computed in O(log n) time using augmented BSTs
  count = 1 + T.sum_less(x)

  T.insert(x, 1 + count) // sets x's auxiliary field to 1 + count
  ret += count // keep track of return value

return ret

答案 2 :(得分:3)

我假设不失一般化,输入A [0 ..(n-1)]由{0,1,...,n-1}中的所有整数组成。

令DP [i] =以A [i]结尾的递增子序列的数量。

我们有了重复:

DP[i] = 1 + \sum_{j < i, A[j] < A[i]} DP[j]

为了计算DP [i],我们只需要计算所有j的DP [j],其中A [j] < A [i]中。因此,我们可以以A的值的升序计算DP阵列。对于所有k,DP [k] = 0,其中A [k]> A [i]中。

问题归结为计算DP [0]和DP [i-1]之和。假设我们已经计算了DP [0]到DP [i-1],我们可以使用Fenwick树计算O(log n)中的DP [i]。

最终答案是DP [0] + DP [1] + ... DP [n-1]。该算法在O(n log n)中运行。

答案 3 :(得分:1)

这是 O(nklogn)解决方案,其中 n 是输入数组的长度, k 是增加子的大小-sequences。它基于solution mentioned in the question

vector<int> values是一个 n 长度数组,是要搜索增加子序列的数组。

vector<int> temp(n); // Array for sorting
map<int, int> mapIndex; // This will translate from the value in index to the 1-based count of values less than it

partial_sort_copy(values.cbegin(), values.cend(), temp.begin(), temp.end());

for(auto i = 0; i < n; ++i){
    mapIndex.insert(make_pair(temp[i], i + 1)); // insert will only allow each number to be added to the map the first time
}

mapIndex现在包含values中所有数字的排名。

vector<vector<int>> binaryIndexTree(k, vector<int>(n)); // A 2D binary index tree with depth k
auto result = 0;

for(auto it = values.cbegin(); it != values.cend(); ++it){
    auto rank = mapIndex[*it];
    auto value = 1; // Number of sequences to be added to this rank and all subsequent ranks
    update(rank, value, binaryIndexTree[0]); // Populate the binary index tree for sub-sequences of length 1

    for(auto i = 1; i < k; ++i){ // Itterate over all sub-sequence lengths 2 - k
        value = getValue(rank - 1, binaryIndexTree[i - 1]); // Retrieve all possible shorter sub-sequences of lesser or equal rank
        update(rank, value, binaryIndexTree[i]); // Update the binary index tree for sub sequences of this length
    }
    result += value; // Add the possible sub-sequences of length k for this rank
}

values的所有 n 元素放入binaryIndexTree的所有 k 维度后。收集到value中的result代表长度 k 的增加子序列的总数。

用于获得此结果的二进制索引树函数是:

void update(int rank, int increment, vector<int>& binaryIndexTree)
{
    while (rank < binaryIndexTree.size()) { // Increment the current rank and all higher ranks
        binaryIndexTree[rank - 1] += increment;
        rank += (rank & -rank);
    }
}

int getValue(int rank, const vector<int>& binaryIndexTree)
{
    auto result = 0;
    while (rank > 0) { // Search the current rank and all lower ranks
        result += binaryIndexTree[rank - 1]; // Sum any value found into result
        rank -= (rank & -rank);
    }
    return result;
}

二进制索引树显然是 O(nklogn),但它能够顺序填充它,从而创建了将其用于解决方案的可能性。

mapIndexvalues中的每个数字创建排名,以便values中的最小数字的排名为1.(例如,如果values为“2 ,3,4,3,4,1“然后mapIndex将包含:”{1,1},{2,2},{3,3},{4,5}“。注意”4 “排名为”5“,因为values

中有2个”3“

binaryIndexTree k 不同的树,等级 x 表示可以由长度 x <形成的增加子字符串的总数/ em>的。 values中的任何数字都可以创建长度为1的子字符串,因此每个元素都会增加它的等级,并且所有等级都高于它。 在较高级别,增加的子串取决于已经存在具有较短长度和较低等级的子串。

因为元素是根据values中的顺序插入到二进制索引树中的,所以values中的出现顺序会被保留,所以如果在binaryIndexTree中插入了一个元素,那么因为它在values

中的当前元素之前

这里有关于二进制索引树如何可用的优秀描述:http://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/

您可以在此处找到代码的可执行版本:http://ideone.com/GdF0me

答案 4 :(得分:0)

让我们举个例子 -

取一个阵列{7,4,6​​,8} 现在,如果您将每个单独的元素也视为子序列,则可以形成的增加子序列的数量为 -
{7} {4} {6} {4,6} {8} {7,8} {4,8} {6,8} {4,6,8}
对于该阵列,可以形成总共 9 增加的子序列 所以答案是9.

代码如下 -

int arr[] = {7, 4, 6, 8};
int T[] = new int[arr.length];

for(int i=0; i<arr.length; i++)
    T[i] = 1;    

int sum = 1;     
for(int i=1; i<arr.length; i++){
    for(int j=0; j<i; j++){
        if(arr[i] > arr[j]){
            T[i] = T[i] + T[j]; 
        }
    }
    sum += T[i];
}
System.out.println(sum);

代码的复杂性是O(N log N)。

答案 5 :(得分:-1)

以Java版为例:

    int[] A = {1, 2, 0, 0, 0, 4};
    int[] dp = new int[A.length];

    for (int i = 0; i < A.length; i++) {
        dp[i] = 1;

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