Two Greedy Algorithms-Leetcode 321 Mosaic Maximum and Leetcode 316 Removal of Duplicate Letters

Greedy algorithm and gradient descent method are similar, each step takes the current optimal solution. However, it is necessary to ensure that the optimal solution is obtained at each step, and finally the global optimal solution is obtained.

First look at Leetcode 316 to remove duplicate letters. The title is as follows:

Given a string containing only lowercase letters, the repeated letters in the string are removed so that each letter appears only once. It is necessary to ensure that the dictionary order of the returned results is minimal (requiring that the relative positions of other characters should not be disturbed).

Example 1:

Input: "bcabc"
Output: "abc"
Example 2:

Input: "cbacdcbc"
Output: "acdb"

The dictionary order is the smallest, so the smaller the letters, the more front they are. You can design a stack and constantly insert characters into it. If the current character is smaller than the top of the stack, the top of the stack character will pop up. But before popping up, you also need to confirm whether there will be this character later, if there is no later, it can not pop up. Specifically, a counter is designed for each letter, and then a marker is used to mark whether or not it is stuffed into the stack. This stack can be implemented directly with string.

class Solution {
public:
    string removeDuplicateLetters(string s) {
        unordered_map<char, int> dict;//key: letter, value: how many letter remaining in current string
        unordered_map<char, bool> exist;//key: letter, value: whether the letter has been put in ans
        for(int i='a'; i<='z'; i++){
            dict[i] = 0;
            exist[i] = false;
        }
        for(char c: s){
            dict[c]++;
        }
        
        string ans = "";
        for(char c: s){
            if(!exist[c]){// if current c not in ans
                while(!ans.empty() && c < ans.back() && dict[ans.back()] > 0){
                    exist[ans.back()] = false;
                    ans.pop_back();
                }
                ans.push_back(c);
                exist[c] = true;
            }
            dict[c]--;
        }
        return ans;
    }
};

For the maximum number of Leetcode 321 splices, first look at the original title:

Given two arrays of length m and N respectively, their elements consist of 0-9, representing the numbers on each of the two natural numbers. Now, from these two arrays, K (k <= m + n) digits are selected and stitched together into a new number, requiring the digits taken from the same array to maintain their relative order in the original array.

Seek the maximum number that satisfies this condition. The result returns an array that represents the length of the maximum number k.

Explanation: Please optimize the time and space complexity of your algorithm as much as possible.

Example 1:

Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[9, 8, 6, 5, 3]
Example 2:

Input:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
Output:
[6, 7, 6, 0, 4]
Example 3:

Input:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
Output:
[9, 8, 9]

We can think about this process by taking I numbers from num1, k-i numbers from num2, and then stitching these two arrays together and traversing I to get the result.

First, we need to pay attention to the range of i. There are two inequalities:

0 <= i <= n1

0 <= k-i <= n2   ==>    k - n2 <= i <= k

Obviously: i needs to satisfy the above two inequalities at the same time, so Max (0, k-n2) <== i <= min (k, n1). Where N1 and N2 are the lengths of two arrays.

The second question is how to extract I numbers from num1. To ensure maximum, so the farther ahead, the bigger. This is exactly the same as leetcode 316. In fact, it is to delete n1-i numbers from nums1 to achieve the minimum dictionary order. The solution is exactly the same as above.

The last question is how to stitch together arrays with length I and length k-i.

Here we use the double pointer method, greedily take the larger value, and then move the corresponding pointer one step back. But there's a problem. What if the two pointers point to the same number? In this way, we can judge the following figures.

For example, pointers are I i, jj, where ii and JJ are used to separate from the above I. If the values at ii and JJ are equal and move back at the same time, until they are not equal, then decide which is larger.

Here's another question. What if ii or jj are equal at the end? This will take the pointer that has not yet reached the end, because there is no end, there may be a larger number than the current number.

The implementation code is as follows:

class Solution {
public:
    bool cmp(vector<int> &nums1, vector<int> &nums2){
        //return true : nums1 > nums2
        if(nums1.size() != nums2.size())
            return nums1.size() > nums2.size();
        for(int i=0; i<nums1.size(); i++){
            if(nums1[i] > nums2[i]){
                return true;
            }
            if(nums1[i] < nums2[i]){
                return false;
            }
        }
        return false;
    }
    vector<int> get(vector<int>& nums, int s){
        vector<int> ans;
        int n = nums.size();
        int todrop = n - s;
        for(int n: nums){
            while(!ans.empty() && n > ans.back() && todrop > 0) {
                ans.pop_back();
                todrop--;
            }
            ans.push_back(n);
        }
        ans.resize(s);
        return ans;
    }
    bool greater(vector<int> nums1, vector<int> nums2, int i, int j){
        if(nums1[i] > nums2[j]){
            return true;
        } else if(nums1[i] < nums2[j]){
            return false;
        }
        while(i < nums1.size() && j < nums2.size() && nums1[i] == nums2[j]){
            i++;
            j++;
        }
        return j == nums2.size() || (i < nums1.size() && nums1[i] > nums2[j]);
    }
    vector<int> merge(vector<int>nums1, vector<int> nums2){
        vector<int> ans;
        int i = 0, j = 0;
        int n1 = nums1.size(), n2 = nums2.size();
        while(i < n1 && j < n2){
            if(nums1[i] > nums2[j]){
                ans.push_back(nums1[i++]);
            } else if(nums1[i] < nums2[j]) {
                ans.push_back(nums2[j++]);
            } else {
                if(greater(nums1, nums2, i, j)){
                    ans.push_back(nums1[i++]);
                } else {
                    ans.push_back(nums2[j++]);
                }
            }
        }
        while(i < n1){
            ans.push_back(nums1[i++]);
        }
        while(j < n2){
            ans.push_back(nums2[j++]);
        }
        return ans;
    }
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<int> ans;
        int n1 = nums1.size(), n2 = nums2.size();
        for(int i = max(0, k-n2); i <= min(n1, k); i++){
            vector<int> tmp = merge(get(nums1, i), get(nums2, k-i));
            ans = cmp(tmp, ans) ? tmp : ans;
        }
        return ans;
    }
};

 

Posted by Ulysses Freeman on Fri, 16 Aug 2019 02:53:18 -0700