Non-decreasing Subsequences

Hello and welcome back to another day of struggling with Leetcode. We have a problem on backtracking ahead of us, so strap in!

Understanding the Problem

Take a look at the problem statement given below.

Given an integer array nums, return all the different possible non-decreasing subsequences of the given array with at least two elements. You may return the answer in any order.

So, we need to return a list of all possible non-decreasing subsequences. Pay attention to the fact that the question specifies non-decreasing, not increasing. So, a subsequence with all equal elements is still a valid answer.

It seems obvious that we can't obtain the final answer without traversing the entire array. In problems like these, we need to check each possible subsequence that can be constructed in the array. Backtracking is the obvious choice for such a problem.

Intuition

For those who are unfamiliar with the concept of backtracking, don't worry I'll be catering to you guys first.

Backtracking is a general algorithm for finding all (or some) solutions to a problem that incrementally builds candidates to the solutions, and abandons a candidate ("backtracks") as soon as it determines that the candidate cannot possibly be completed to a valid solution.

Let's see how this definition relates to the given problem. We need to find all subsequences of the given array which satisfy the given constraint of being non-decreasing. An intuitive solution that seems to come to mind is to incrementally add each valid element to the answer, and ignore the invalid elements.

But, since the question asks for all non-decreasing subsequences, we need to consider both choosing and ignoring a valid element. Let's take an example to see what this means.

Example 1:

Input: nums = [4,6,7,7]
Output: [[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

Here, we need to choose and ignore the element 6 in order to get all the non-decreasing subsequences.

Let's see the algorithm we'll need to follow to solve this problem.

Algorithm

  1. Initialize a set of arrays set<vector<int>> ans that will store the non-decreasing subsequences. The reason we use a set is so that we don't have to worry about duplicate subsequences being added to our answer.

  2. Initialize an array vector<int> curr that will store the current subsequence we're building.

  3. Define a recursive helper function that will perform the backtracking operation. It takes the following parameters.

    1. vector<int>& nums: the input array.

    2. set<vector<int>>& ans: the set of non-decreasing subsequences.

    3. vector<int>& curr: array to store the current subsequence.

    4. int idx: the current element's index.

  4. The base condition for the helper function will be when we reach the end of the input array, that is, the current index idx = nums.size() . In this case, we check if the size of the current subsequence we've built is greater than two. If it is, then we add it to our answer.

  5. We need to consider two cases:

    1. Include the current element in the subsequence if it satisfies the constraint that the subsequence remains non-decreasing after adding the element.

    2. Exclude the current element and move on to the next index.

Try to code up a solution using this algorithm in a language of your choice before moving on to the next section.

Coding Up The Solution

Here's the full solution to this problem.

class Solution {
public:
    void helper(vector<int>& nums,set<vector<int>>& ans,vector<int>& curr, int start){
        //base condition
        if(start == nums.size()){
            if(curr.size() >= 2)
                ans.insert(curr);
            return;
        }

        if(curr.empty() or curr.back() <= nums[start]){
            //include the current element in the subsequence
            curr.push_back(nums[start]);
            //call the helper function recursively to move on to the next element in the array
            helper(nums,ans,curr,start+1);
            //backtrack
            curr.pop_back();
        }
        //exclude the current element from the subsequence and move on to the next element
        helper(nums,ans,curr,start+1);
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {

        set<vector<int>> store;
        vector<int> curr;

        helper(nums,store,curr,0);
        return vector(store.begin(),store.end());
    }
};

That's all for today. I hope you had fun learning about backtracking! Stay tuned for more solutions and please subscribe to my newsletter to never miss out on an article!

Cheers!