您可能听说过有关找到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)))
,请告诉我关于那个。
最后:这个问题不是作业。在我的演讲中提到了一个问题,即增加的子序列越来越长,我开始考虑给定序列中所有增加子序列的一般概念。
答案 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],我们只需要计算所有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),但它能够顺序填充它,从而创建了将其用于解决方案的可能性。
mapIndex
为values
中的每个数字创建排名,以便values
中的最小数字的排名为1.(例如,如果values
为“2 ,3,4,3,4,1“然后mapIndex
将包含:”{1,1},{2,2},{3,3},{4,5}“。注意”4 “排名为”5“,因为values
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];
}
}
}