For the Longest Increasing Subsequence problem I envisioned keeping a DP array that is always in order keeping the max value at the farthest end. Something that would look like this:
{1, 1, 2, 3, 3, 4, 5, 6, 6, 6}
The thought process I followed to produce my first incorrect solution was, we want to look at the entire array starting with only the first element, calculate the LIS, then incrementally add on a value to the end of our array. While doing this, we incrementally calculate the LIS in our DP array to the LIS of the old subarray plus the new element we added on. This means at index i
of the dp
array exists the value of the LCS of the subarray of length i
.
More clearly put
array => {5, 6, 7, 1, 2, 3, 4}
dp => {1, 2, 3, 3, 3, 3, 4}
This way the very last entry of the DP array will be the LIS of the current array. This would act as our invariant, so when we get the end, we can be assured that the last value is the only one we need. It then dawned on me that while we're traversing an array with a DP kinda feel, the next value does not depend on any of the previously tabulated values in the array, so this method is the same as maintaining a maxLIS
variable, a pattern I've seen in many O(n)
solutions. So my closest-to-correct solution is as follows:
1.) Save a copy of the input array/vector as old
2.) Sort the original input array
3.) Traverse the sorted array, incrementing a variable longest
by one every time the next value (which should be larger than the current`) appears before the current in the original array.
4.) Return longest
The code would be ~this:
int lengthOfLIS(vector<int>& seq) {
if (!seq.size()) return 0;
vector<int> old = seq;
sort(seq.begin(), seq.end());
int longest = 1;
for (int i = 1; i < seq.size(); ++i) {
if (seq[i] > seq[i-1] && find(old.begin(), old.end(), seq[i]) - old.begin() > find(old.begin(), old.end(), seq[i-1]) - old.begin()) longest++;
}
return longest;
}
Where we have the find
method (I'm assuming a linear operation) we could make a constant operation by just making a data structure to store the original index of the value along with the the value itself so we don't have to do any traversing to find the index of an element in the original array (old
). I believe this would be an O(nlog(n))
solution however fails with this input array: [1,3,6,7,9,4,10,5,6]
. CHECK HERE
Finally I did some research and I found that all solution guides I have read sneak in the fact that their solution keeps the values of their DP array not in order, but instead like this: A value in the DP array represents the length of an increasing subsequence with the last value of the subsequence being the value of originalArray[index]
.
More clearly put,
array => {5, 6, 7, 1, 2, 3}
dp => {1, 2, 3, 1, 2, 3}
Here, where 5 is the last value of an increasing subsequence, no values come before it so it must be of length 1. If 6 is the last value of an increasing subsequence, we must look at all values before it to determine how long a subsequence ending with 6 can be. Only 5 can come before it, thus making the longest increasing subsequence thus far 2. This continues, and you return the maximum value in the DP array. Time complexity for this solution is O(n^2)
, standard naive solution.
Questions:
I'm curious as to how I can think about this problem correctly. I want to fine-tune my thought process so that I can come up with an optimal solution from scratch (that's the goal at least) so I'd like to know
1.) What property of this problem should've triggered to me to use a DP array differently than how I would've used it? In hindsight, my original way was simply equivalent to keeping a max variable but even then I struggle seeing a property of this problem that would trigger the thought `Hey, the value of an entry in my DP array at index i should be the length of the increasing subsequence ending with originalArray[i]. I'm struggling to see how I should've come up with that.
2.) Is it possible to get my proposed O(nlog(n))
solution to work? I know an O(nlog(n))
solution exists, but since I can't get mine working I think I need a nudge in the right direction.