LeetCode: Wiggle Sub-sequence (Dynamic Programming)
https://leetcode.com/problems/wiggle-subsequence/, problem description:
A naive approach would try all the permutations, on N! (intractable for large N's). The Dynamic Programming (DP) idea to solve this problem would be the following:
A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.
For example,
[1,7,4,9,2,5]
is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5]
and [1,7,4,5,5]
are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.
Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.
Examples:
Input: [1,7,4,9,2,5] Output: 6 The entire sequence is a wiggle sequence. Input: [1,17,5,10,13,15,10,5,16,8] Output: 7 There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. Input: [1,2,3,4,5,6,7,8,9] Output: 2
A naive approach would try all the permutations, on N! (intractable for large N's). The Dynamic Programming (DP) idea to solve this problem would be the following:
- DP assumes that you know the solution for sub-problems, and then use those solutions to solve the current problem
- We can assume that we know the solutions for all values from 1 to N-1
- Initially the solutions for each number from 1..N is 1
- The idea is that for each number from "i" 1..N-1 we'll have the best solution ending at index "i" wiggling positively, and ending at index "i" wiggling negatively
- Then for each number "i", go to all the best solutions from 1..i-1 and based on the previous best solutions update the current solution for "i"
- At the same time keep track of the best solution seen
Not extremely fast (not O(n)), but O(n^2). Code down below, cheers, Marcelo.
public class Solution {
class Item
{
public int topLastNegative = 0;
public int topLastPositive = 0;
public Item(int topLastNegative, int topLastPositive)
{
this.topLastNegative = topLastNegative;
this.topLastPositive = topLastPositive;
}
}
public int WiggleMaxLength(int[] nums) {
if(nums == null || nums.Length == 0) return 0;
Item[] items = new Item[nums.Length];
int solution = 1;
//Init the items with the best solution thus far per item
for (int i = 0; i < nums.Length; i++)
items[i] = new Item(1, 1);
for (int i = 0; i < nums.Length; i++)
{
for (int j = 0; j < i; j++)
{
//Switch the current "i" in case this is a better solution
if (nums[i] > nums[j] && items[j].topLastNegative + 1 > items[i].topLastPositive) items[i].topLastPositive = items[j].topLastNegative + 1;
else if (nums[i] < nums[j] && items[j].topLastPositive + 1 > items[i].topLastNegative) items[i].topLastNegative = items[j].topLastPositive + 1;
//Change the global solution variable whenever applicable
if (items[i].topLastNegative > solution) solution = items[i].topLastNegative;
else if (items[i].topLastPositive > solution) solution = items[i].topLastPositive;
}
}
return solution;
}
}
Very nice, Marcelo! I decided to use parallel arrays to decrease memory usage and instead of storing topLast* elements I story only best + the last wiggle turn but the general idea is the same.
ReplyDeleteclass Solution {
private:
template constexpr char signOf(T val) const {
return (T(0) < val) - (val < T(0));
}
public:
int wiggleMaxLength(const vector& nums) {
if (nums.size() < 2) return nums.size();
vector lengths(nums.size(), 1);
vector signs(nums.size(), 0);
for (int i = 1; i < nums.size(); ++i) {
for (int j = 0; j < i; ++j) {
if (nums[i] == nums[j]) continue;
char sign = signOf(nums[j] - nums[i]);
if (lengths[i] < lengths[j] + 1 && signs[j] != sign) {
lengths[i] = lengths[j] + 1;
signs[i] = sign;
}
}
}
return *max_element(lengths.cbegin(), lengths.cend());
}
};
http://ideone.com/OD23yJ
Hereby you've proven yet another time that you rock at DP ;)