Showing posts with label Lintcode. Show all posts
Showing posts with label Lintcode. Show all posts

Sunday, October 20, 2019

Lintcode 848: Minimize Max Distance to Gas Station

https://www.lintcode.com/problem/minimize-max-distance-to-gas-station/description

On a horizontal number line, we have gas stations at positions stations[0], stations[1], ..., stations[N-1], where N = stations.length.
Now, we add K more gas stations so that D, the maximum distance between adjacent gas stations, is minimized.
Return the smallest possible value of D.

Example

Example 1:
Input:stations = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],K = 9
Output:0.50
Explanation:The distance between adjacent gas stations is 0.50
Example 2:
Input:stations = [3,6,12,19,33,44,67,72,89,95],K = 2
Output:14.00
Explanation:construction of gas stations at 86 locations

Notice

1.stations.length will be an integer in range [10, 2000].
2.stations[i] will be an integer in range [0, 10^8].
3.K will be an integer in range [1, 10^6].
4.Answers within 10^-6 of the true value will be accepted as correct.

Notes:

This question can be solved by the binary search method.

Apparently, the question is asking for a min of maximum. Let say minMax, when val >= minMax, we can always finish one event; otherwise, it cannot be done. And val is apparently in a range.

So it is a binary search!

See the code below:

class Solution {
public:
    /**
     * @param stations: an integer array
     * @param k: an integer
     * @return: the smallest possible value of D
     */
    double minmaxGasDist(vector<int> &stations, int k) {
        // Write your code here
        sort(stations.begin(), stations.end());
        double epslon = 0.00001, left = epslon, right = stations.back();
        while(left + epslon < right) {
            double mid = left + (right - left) / 2;
            if(!isValid(stations, k, mid)) left = mid;
            else right = mid;
        }
        return left;
    }

private:
    bool isValid(vector<int>& gs, int k, double mid) {
        int ct = 0;
        double sum = 0;
        for(int i=0; i+1<gs.size(); ++i) {
            double dis = (double) (gs[i+1] - gs[i]);
            ct += (int)(dis/mid);
        }
        return ct <= k;
    }
};

Wednesday, October 16, 2019

Lintcode 79: Longest Common Substring

https://www.lintcode.com/problem/longest-common-substring/description

Given two strings, find the longest common substring.
Return the length of it.

Example

Example 1:
 Input:  "ABCD" and "CBCE"
 Output:  2
 
 Explanation:
 Longest common substring is "BC"


Example 2:
 Input: "ABCD" and "EACB"
 Output:  1
 
 Explanation: 
 Longest common substring is 'A' or 'C' or 'B'

Challenge

O(n x m) time and memory.

Notice

The characters in substring should occur continuously in original string. This is different with subsequence.

Notes:

A classic DP problem. The only place needing to pay attention is that: this question is for substring, not subsequence.

let dp[i][j] represents the longest common substring ending with A[i] and A[j], respectively.

Thus,

when A[i] == A[j], dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = 0;  (that is the different part to sequnce...)

See the code below:

class Solution {
public:
    /**
     * @param A: A string
     * @param B: A string
     * @return: the length of the longest common substring.
     */
    int longestCommonSubstring(string &A, string &B) {
        // write your code here
        int m = A.size(), n = B.size(), res = 0;
        vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
        //dp[i][j]: the lcs ending with A[i] & A[j], respectively.
        for(int i=1; i<=m; ++i) {
            for(int j=1; j<=n; ++j) {
                if(A[i-1] == B[j-1]) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                    res = max(res, dp[i][j]);
                }
            //    else dp[i][j] = 0; //not necessary
            }
        }
        return res;
    }
};


We can further compress the space.

See the code below:

class Solution {
public:
    /**
     * @param A: A string
     * @param B: A string
     * @return: the length of the longest common substring.
     */
    int longestCommonSubstring(string &A, string &B) {
        // write your code here
        int m = A.size(), n = B.size(), res = 0;
        vector<int> dp(n+1, 0);
        //dp[i][j]: the lcs ending with A[i] & A[j], respectively.
        for(int i=1; i<=m; ++i) {
            for(int j=n; j>=1; --j) {
                if(A[i-1] == B[j-1]) {
                    dp[j] = dp[j-1] + 1;
                    res = max(res, dp[j]);
                }
                else dp[j] = 0;//must have
            }
        }
        return res;
    }
};

Thursday, September 26, 2019

Lintcode 829: Word Pattern II

https://www.lintcode.com/problem/word-pattern-ii/description


Notes:

This question is a very good back-tracking problem.

The key for back-tracking is how to recover (or go back) the previous condition.

In this question, we need to correlate the letter in pattern with the substring in the string. So we need two hash maps to record this bijection relationship.

When we plan to move from one step to the next step, we need to make judgement whether we should go or not.

(1) If we can make a decision right away without further exploring, we should stop immediately; and then move on to the next possible step;

(2) if we cannot tell right now, we have to continue the exploring to the next step (this is called dfs);

(3) before we move on to the next step, we should know exactly what kinds of changes have been made to the key parameters in order to move onto the next step (and this is the key to this question);

(4) if the result from the further exploring is not valid, we need to recover the changes made in step (3) (this is called back-tracking);

(5) if the result from the further exploring is valid, we find a valid solution and can return true;

(6) if we have tried all the possible paths but still cannot find a valid one, return false.


See the code below:

class Solution {
public:
    /**
     * @param pattern: a string,denote pattern string
     * @param str: a string, denote matching string
     * @return: a boolean
     */
    bool wordPatternMatch(string &pattern, string &str) {
        // write your code here
        unordered_map<char, string> m1;
        unordered_map<string, char> m2;
        return dfs(pattern, 0, str, 0, m1, m2);
    }

private:
    bool dfs(string &s1, int id1, string &s2, int id2, unordered_map<char, string> &m1, unordered_map<string, char> &m2) {
        if(id1 == s1.size()) return id2 == s2.size();
        if(id2 == s2.size()) return id1 == s1.size();
        if((int)s1.size() - id1 > (int)s2.size() - id2) return false;
        char c = s1[id1];
        for(int i=id2; i<s2.size(); ++i) {
            string s = s2.substr(id2, i-id2+1);
            if(m1.count(c) && m1[c] != s) continue;
            if(m2.count(s) && m2[s] != c) continue;
            bool f1 = false, f2 = false;
            if(!m1.count(c)) {//f1 used to label the real change in m1;
                m1[c] = s;
                f1 = true;
            }
            if(!m2.count(s)) {//f2 used to label the real change in m2;
                m2[s] = c;
                f2 = true;
            }
            if(dfs(s1, id1+1, s2, i+1, m1, m2)) return true;
            if(f1) m1.erase(c);
            if(f2) m2.erase(s);
        }
        return false;
    }
}

Lintcode 386: Longest Substring with At Most K Distinct Characters

https://www.lintcode.com/problem/longest-substring-with-at-most-k-distinct-characters/description


Notes:

This question is labeled as a hard question in Leetcode, but actually it is not that hard, many be a medium one.

The basic idea is to use a sliding window. When the total kinds of the letter is less than k, we just continue, and count both kind and number of each kind of letter. A hash map can do this.

When the number of kind is larger than k, we need to move the left side of the window, until the number of kind is no more larger than k. Then we can record the length of this valid string, and update the global maximum.

See the code below:

class Solution {
public:
    /**
     * @param s: A string
     * @param k: An integer
     * @return: An integer
     */
    int lengthOfLongestSubstringKDistinct(string &s, int k) {
        // write your code here
        int res = 0, left = 0, len = s.size(), ct = 0;
        if(!k) return res;
        unordered_map<char, int> mp;
        for(int i=0; i<len; ++i) {
            if(!mp.count(s[i]) || mp[s[i]] == 0) {
                ++ct;
                ++mp[s[i]];
                if(ct>k) {
                    while(ct>k) {
                        --mp[s[left++]];
                        if(mp[s[left-1]] == 0) --ct;
                    }
                }
            }
            else ++mp[s[i]];
            res = max(res, i - left + 1);
        }
        return res;
    }
};


Here is another way to write the code, which is shorter. But we need to erase element from a hash map. 

See the code blow:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int res = 0, left = 0, len = tree.size();
        unordered_map<int, int> mp;
        for(int i=0; i<len; ++i) {
            ++mp[tree[i]];
            while(mp.size() > 2) {
                 if(--mp[tree[left]] == 0) mp.erase(tree[left]);
                 ++left;
            }
            res = max(res, i - left + 1);
        }
        return res;
    }
};

Now let us focus one the interesting part: what do the above two codes run in O(N)? It seems at some positions, we need to slide the left side of the window many times.

But if we look at the hash map, and it is clear that every element will be pushed in exactly once, and not every element (less than N) will be pop out. Thus, the total operate is less than 2N. So it is O(N).

[update: 12-07-2019] here is another version of code, which is more concise:

class Solution {
public:
    int lengthOfLongestSubstringKDistinct(string &s, int k) {
        int res = 0, left = 0, len = s.size(), ct = 0;
        if(!k) return res;
        unordered_map<char, int> mp;
        for(int i=0; i<len; ++i) {
            ++mp[s[i]];
            if(mp[s[i]] == 1) ++ct;
            while(ct>k) {
                --mp[s[left++]];
                if(mp[s[left-1]] == 0) --ct;
            }
            res = max(res, i - left + 1);
        }
        return res;
    }
};

Wednesday, September 25, 2019

Lintcode 874: Maximum Vacation Days

https://www.lintcode.com/problem/maximum-vacation-days/description

Notes:

This question seems very complicated, but actually the logic to this question is very simple. Find a path with the maximum sum (or vacation days.) with some limitations.

One is that we can only move finite steps (number of the weeks), the other is that we can stay at the same node (or city).

One method is DFS + memo.

See the code below:

class Solution {
public:
    /**
     * @param flights: the airline status from the city i to the city j
     * @param days: days[i][j] represents the maximum days you could take vacation in the city i in the week j
     * @return: the maximum vacation days you could take during K weeks
     */
    int maxVacationDays(vector<vector<int>> &flights, vector<vector<int>> &days) {
        // Write your code here
        int m = flights.size(), a = days.size(), b = days[0].size();
        vector<vector<int>> g(m);
        for(int i=0; i<m; ++i) {
            g[i].push_back(i);
            for(int j=0; j<m; ++j) {
                if(flights[i][j] == 1) g[i].push_back(j);
            }
        }
        vector<vector<int>> memo(m, vector<int>(b, -1));
        return dfs(0, 0, g, days, memo);
    }

private:
    int dfs(int st, int wks, vector<vector<int>> &g, vector<vector<int>> &dys, vector<vector<int>> &memo) {
        if(wks == dys[0].size()) return 0;
        if(memo[st][wks] != -1) return memo[st][wks];
        int t = 0;
        for(auto &a : g[st]) {
            t = max(t, dys[a][wks] + dfs(a, wks+1, g, dys, memo));//find the max;
        }
        return memo[st][wks] = t;
    }
};

Lintcode 43: Maximum Subarray III

https://www.lintcode.com/problem/maximum-subarray-iii/description


Noes:

This question is said to be a classical dp question, but I do not think I had seen this before.

Anyway, the method used here is the so-called "local-global" maximum algorithm.

local[i][j]: represents the maximum value achieved using the first i elements with j non-overlapping groups and ending with the ith element;

global[i][j]: represents the maximum value achieved using the first i elements with j non-overlapping groups  and not necessarily ending with the ith element;

local[j][j] = max (global[i-1][j-1], local[i-1][j]) + nums[i]

The local[i][j] must ends with the ith element, so we need to add it. The ith element can form a group (sub-array) by itself; or it can joins the previous group, which corresponds to the first and the second terms;

global[i][j] = max(global[i-1][j], local[i][j])

For global[i][j], we may or may not use the ith element. If used, it should be local[i][j]; otherwise it is global[i-1][j].

In addition, we need to pay attention to the initialization:

(1) When j > i, it is impossible to form j different non-overlapping groups. In this case, it can be initialized as INT_MIN;

(2) local[i][0] can be initialized as INT_MIN as well, since it is impossible to form this kind of condition;

(3) global[i][0] can be initialized as 0. We simply do no choose any element.

See the code below:

class Solution {
public:
    /**
     * @param nums: A list of integers
     * @param k: An integer denote to find k non-overlapping subarrays
     * @return: An integer denote the sum of max k non-overlapping subarrays
     */
    int maxSubArray(vector<int> &nums, int k) {
        // write your code here
        int len = nums.size();
        vector<vector<int>> local(len+1, vector<int>(k+1, INT_MIN)), global(len+1, vector<int>(k+1, INT_MIN));
        //local[i][j]: the maximum sum of j non-overlapping subarrays ending with the ith elements;
        //global[i][j]: the maximum sum of j no-voerlapping subarrays not necessary ending with the ith elements.
        //local[i][j] = max(global[i-1][j-1], local[i-1][j]) + nums[i];
        //global[i][j] = max(global[i-1][j], local[i][j]);
        for(int i=0; i<=len; ++i) global[i][0] = 0;
        for(int i=1; i<=len; ++i) {
            for(int j=1; j<=min(k, i); ++j) {
                local[i][j] = max(global[i-1][j-1], local[i-1][j]) + nums[i-1];
                global[i][j] = max(global[i-1][j], local[i][j]);
            }
        }
        return global[len][k];
    }
};

Tuesday, September 24, 2019

Lintcode 564: Backpack VI

https://www.lintcode.com/problem/combination-sum-iv/description


Notes:

As indicated by the title of the question, this problem belongs to backpack or knapsack problems.

But this question does not ask how many ways to fill up the bag, instead, it asks how many combinations to fill up the bag.

Or another way to say this,  the order of picking up one elements does matter!!!

Let say dp[v] represents the ways of combinations to fill it up.

Then,

dp[v] = sum {dp[v - val[i]]}, when v >= val[i].

The meaning is straightforward: we just add another val[i] element at the end of all the previous results. And each of them will still be a unique combination, so it still can be counted as one solution.

The key is that: we must put the first loop as the volume increasing from 1 to v; the inner loop is the elements. This is different from the typical knapsack problem, which is just the opposite.

So what is the difference?

The different is that: if we put the elements in the outside loop, then the order of elements inside the solution does not matter. Or we are calculating how many way to fill up the bag.

This question asks for how may combinations, so we need to pay attention to the relative orders of the elements.

See the code below:

class Solution {
public:
    /**
     * @param nums: an integer array and all positive numbers, no duplicates
     * @param target: An integer
     * @return: An integer
     */
    int backPackVI(vector<int> &nums, int target) {
        // write your code here
        vector<int> dp(target+1, 0);
        dp[0] = 1; //must have
        for(int i=1; i<=target; ++i) {
            for(int j=0; j<nums.size(); ++j) {
                if(i >= nums[j]) dp[i] += dp[i - nums[j]];
            }
        }
        return dp[target];
    }
};

To better understand the subtle difference discussed above, we can run a small example: [1, 2], and target = 3.

To this question, the answer is 3. [1, 1, 1], [1, 2], and [2, 1].

If we ask for how many ways to fill-up the bag ignoring the relative orders. then it is 2. [1, 1, 1], and [1, 2].

Lintcode 617: Maximum Average Subarray II

https://www.lintcode.com/problem/maximum-average-subarray-ii/note/171180

Description

中文English
Given an array with positive and negative numbers, find the maximum average subarray which length should be greater or equal to given length k.

It's guaranteed that the size of the array is greater or equal to k.

Example

Example 1:
Input:
[1,12,-5,-6,50,3]
3
Output:
15.667
Explanation:
 (-6 + 50 + 3) / 3 = 15.667
Example 2:
Input:
[5]
1
Output:
5.000


Notes:

This question is not an easy question, but greedy can solve it.

One way to think about this question is that: let's say we are now at the ith element position. If we put the ith element into consideration, we will have i more subarrays ending with the ith element. But we only care about the ones with a length no less than k.

So just need to record the previous sum results ending with the (i-1)th element and the corresponding length (since we need the average of them). Then update these results in both sum and length: sum will be added by nums[i], and length will be added by 1. And we will have a new subarray with size of k ending at the ith element. So we also need to take this into account.

One improvement is that, we can maintain a decreasing array in the sum. Since that the subarrays with the sum is smaller and the length is longer cannot give a larger average. So them can be ruled out directly.

During the scanning, we update the maximum average.

See the code below:

class Solution {
public:
    /**
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    double maxAverage(vector<int> &nums, int k) {
        // write your code here
        //sum: the sum of the last k elements ending with the (i-1)th element;
        double res = 0, sum = 0;
        int len = nums.size();
        stack<pair<double, double>> sk;
        for(int i=0; i<k; ++i) sum += (double) nums[i];
        res = sum/(double)k;
        sk.push({sum, k});
        for(int i=k; i<len; ++i) {
            sum += (double)nums[i] - (double)nums[i-k];
            vector<pair<double, double>> vs;
            while(sk.size()) {
                vs.push_back(sk.top());
                sk.pop();
            }
            for(auto &a : vs) {
                a.first += (double) nums[i];
                a.second += 1;
            }
            vs.push_back({sum, k});
            for(auto &a : vs) {
                auto t = a.first/a.second;
                if(res < t) res = t;
                if(sk.empty() || sk.top().first/sk.top().second > t) sk.push(a);
                else {
                    while(sk.size() && sk.top().first/sk.top().second <= t) sk.pop();
                    sk.push(a);
                }
            }
        }
        return res;
    }
};


Follow up:

This question can be modified a little bit: what is the maximum subarray sum with the size of the subarray no less than k?

The idea is very similar to the above one, but it is much easier to code, since we just need to save the largest sum from all previous subarray ending with the (i-1)th element.