I have the following algorithm which works well
I tried explaining it here for myself http://nemo.la/?p=943 and it is explained here http://www.geeksforgeeks.org/lon
Just apply the longest increasing sub-sequence algorithm to ordered pair (A[i], i), by using a lexicographic compare.
My Java version:
public static int longestNondecreasingSubsequenceLength(List<Integer> A) {
int n = A.size();
int dp[] = new int[n];
int max = 0;
for(int i = 0; i < n; i++) {
int el = A.get(i);
int idx = Arrays.binarySearch(dp, 0, max, el);
if(idx < 0) {
idx = -(idx + 1);
}
if(dp[idx] == el) { // duplicate found, let's find the last one
idx = Arrays.binarySearch(dp, 0, max, el + 1);
if(idx < 0) {
idx = -(idx + 1);
}
}
dp[idx] = el;
if(idx == max) {
max++;
}
}
return max;
}
To find the longest non-strictly increasing subsequence, change these conditions:
- If
A[i]
is smallest among all end candidates of active lists, we will start new active list of length1
.- If
A[i]
is largest among all end candidates of active lists, we will clone the largest active list, and extend it byA[i]
.- If
A[i]
is in between, we will find a list with largest end element that is smaller thanA[i]
. Clone and extend this list byA[i]
. We will discard all other lists of same length as that of this modified list.
to:
- If
A[i]
is smaller than the smallest of all end candidates of active lists, we will start new active list of length1
.- If
A[i]
is largest among all end candidates of active lists, we will clone the largest active list, and extend it byA[i]
.- If
A[i]
is in between, we will find a list with largest end element that is smaller than or equal toA[i]
. Clone and extend this list byA[i]
. We will discard all other lists of same length as that of this modified list.
The fourth step for your example sequence should be:
10
is not less than 10
(the smallest element). We find the largest element that is smaller than or equal to 10
(that would be s[0]==10
). Clone and extend this list by 10
. Discard the existing list of length 2. The new s
becomes {10 10}
.
Your code nearly works except a problem in your binary_search()
function, this function should return the index of the first element that's greater than the target element(x) since you want the longest non-decreasing sequence. Modify it to this, it'll be OK.
If you use c++, std::lower_bound()
and std::upper_bound()
will help you get rid of this confusing problem. By the way, the if statement"if (height[s[k]] >= height[i])
" is superfluous.
int binary_search(int first, int last, int x) {
while(last > first)
{
int mid = first + (last - first) / 2;
if(height[s[mid]] > x)
last = mid;
else
first = mid + 1;
}
return first; /* or last */
}
A completely different solution to this problem is the following. Make a copy of the array and sort it. Then, compute the minimum nonzero difference between any two elements of the array (this will be the minimum nonzero difference between two adjacent array elements) and call it δ. This step takes time O(n log n).
The key observation is that if you add 0 to element 0 of the original array, δ/n to the second element of the original array, 2δ/n to the third element of the array, etc., then any nondecreasing sequence in the original array becomes a strictly increasing sequence in the new array and vice-versa. Therefore, you can transform the array this way, then run a standard longest increasing subsequence solver, which runs in time O(n log n). The net result of this process is an O(n log n) algorithm for finding the longest nondecreasing subsequence.
For example, consider 30, 20, 20, 10, 10, 10, 10. In this case δ = 10 and n = 7, so δ / n ≈ 1.42. The new array is then
40, 21.42, 22.84, 14.28, 15.71, 17.14, 18.57
Here, the LIS is 14.28, 15.71, 17.14, 18.57, which maps back to 10, 10, 10, 10 in the original array.
Hope this helps!