I’m working on meta algorithms and the optimized O(nlogn) solution for longest nondecreasing subsequence stumped me.
template <typename T>
int longest_nondecreasing_subsequence(vector<T> &A)
{
vector<T> tail_values;
for(const T &a: A)
{
auto it = upper_bound(tail_values.begin(), tail_values.end(), a);
if( it == tail_values.end())
{
tail_values.emplace_back(a);
}
else
{
*it = a;
}
}
return tail_values.size();
}
As I understand the above problem, upper_bound(first, last, val)
works on only sorted array as it is trying to do binary search for first value greater than val
Suppose we pass following vector as input: vector<int> A{4,2,7,8,1}
The result tail_values
would be tail_values = {1,7,8}
I understand the tail_values.size()
is the right number for answer but {1,7,8}
is not the actual increasing subsequence. Also the upper_bound
worked because every time we modified tail_values
we made sure it remains sorted.
but Its just hard to get an intuition or the thought process behind this answer for me. Could you explain it better? The theoretical explanation in the book doesn’t make sense to me.
EDIT: just to be clear, theoretical explanation talks about all the steps in the code including modifying the tail_values
array. What is unclear to me is how can one ever think that size of {1,7,8}
is the right answer to an input array which has elements in the order{4,2, 7,8,1},
even though it is the right answer. I hope you understand what I mean.