Showing posts with label Airbnb. Show all posts
Showing posts with label Airbnb. Show all posts

Thursday, February 20, 2020

Leetcode 755 @ Pouring Water and Print out drop graph

755. Pour Water
Medium
We are given an elevation map, heights[i] representing the height of the terrain at that index. The width at each index is 1. After V units of water fall at index K, how much water is at each index?
Water first drops at index K and rests on top of the highest terrain or water at that index. Then, it flows according to the following rules:

  • If the droplet would eventually fall by moving left, then move left.
  • Otherwise, if the droplet would eventually fall by moving right, then move right.
  • Otherwise, rise at it's current position.
  • Here, "eventually fall" means that the droplet will eventually be at a lower level if it moves in that direction. Also, "level" means the height of the terrain plus any water in that column.
    We can assume there's infinitely high terrain on the two sides out of bounds of the array. Also, there could not be partial water being spread out evenly on more than 1 grid block - each unit of water has to be in exactly one block.
    airbnb还要求把水滴过程给打印出来,也不难,写了个过程,看代码。主要是增加了一个全局的water数组来track哪些地方有水滴,有几滴。

    /******************************************************************************
    
                                  Online C++ Compiler.
                   Code, Compile, Run and Debug C++ program online.
    Write your code in this editor and press "Run" button to compile and execute it.
    
    *******************************************************************************/
    
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Solution {
        vector<int> water;
        vector<int> heights;
        int pos, drops, len;
    public:
        Solution(vector<int> h, int V, int K)
        {
            heights = h;
            drops = V;
            pos = K;
            len = 0;
            water.resize(h.size(), 0);
        }
        void pourWater() {
            if (heights.empty()) return;
           
            for (int i = 0; i<drops; i++)
            {
                int j = pos;
                while(j > 0 && heights[j-1]+water[j-1] <= heights[j] + water[j]) j--;
                while(j < heights.size()-1 && heights[j+1] + water[j+1] <= heights[j] + water[j]) j++;
                while(j > pos && heights[j-1] + water[j-1] <= heights[j] + water[j]) j--;
                water[j]++;
                print();
            }
        }
        void print()
        {
            
            for (int i = 0; i<heights.size(); i++)
            {
                if (heights[i]+water[i] > len)
                    len = heights[i]+water[i];
            }    
            
            vector<int> tmp = water;
            for (int i = 0; i<len; i++)
            {
                for (int j = 0; j<heights.size();j++)
                {
                    if (tmp[j] > 0 && tmp[j]+heights[j] > len-i-1)
                    {
                        cout << 'W';
                        tmp[j]--;
                        continue;
                    }
                    if (heights[j] > len-i-1)
                    {
                        cout << 'X';
                        continue;
                    }
                    cout << ' ';
                }
                cout << endl;
            }
            cout<<endl;
        }
    };
    
    int main()
    {
        cout<<"Hello World" << endl;
        vector<int> heights = {2,1,1,2,1,2,2};
        int V = 4;
        int K = 3;
        Solution sol(heights, V, K);
        sol.print();
        sol.pourWater();
        return 0;
    }
    

    Tuesday, February 11, 2020

    Leetcode 1166 @ Design System Files

    1166. Design File System
    Medium
    You are asked to design a file system which provides two functions:
    • createPath(path, value): Creates a new path and associates a value to it if possible and returns True. Returns False if the path already exists or its parent path doesn't exist.
    • get(path): Returns the value associated with a path or returns -1 if the path doesn't exist.
    The format of a path is one or more concatenated strings of the form: / followed by one or more lowercase English letters. For example, /leetcode and /leetcode/problems are valid paths while an empty string and / are not.
    Implement the two functions.
    Please refer to the examples for clarifications.

    Example 1:
    Input: 
    ["FileSystem","createPath","get"]
    [[],["/a",1],["/a"]]
    Output: 
    [null,true,1]
    Explanation: 
    FileSystem fileSystem = new FileSystem();
    
    fileSystem.createPath("/a", 1); // return true
    fileSystem.get("/a"); // return 1
    
    Example 2:
    Input: 
    ["FileSystem","createPath","createPath","get","createPath","get"]
    [[],["/leet",1],["/leet/code",2],["/leet/code"],["/c/d",1],["/c"]]
    Output: 
    [null,true,true,2,false,-1]
    Explanation: 
    FileSystem fileSystem = new FileSystem();
    
    fileSystem.createPath("/leet", 1); // return true
    fileSystem.createPath("/leet/code", 2); // return true
    fileSystem.get("/leet/code"); // return 2
    fileSystem.createPath("/c/d", 1); // return false because the parent path "/c" doesn't exist.
    fileSystem.get("/c"); // return -1 because this path doesn't exist.
    

    Constraints:
    • The number of calls to the two functions is less than or equal to 10^4 in total.
    • 2 <= path.length <= 100
    • 1 <= value <= 10^9
    NOTE: create method has been changed on August 29, 2019 to createPath. Please reset to default code definition to get new method signature.
    这道题思路很简单,只要把出现过的path和对应的文件当作key-value pair都存起来就好了,那么很容易想到hashmap来做。由于这里有层级递推地查找,也是个应用trie的case。所以hashmap or trie都可以做。
    只是用trie的话,trieNode里面存的值,和"/"的处理需要注意。

    问题还有follow up, 就是设计一个watch function, 是callback的功能,可以看一看java的callback。Callback就是说,如果在某个被watch的path下面新添了任何文件夹,或者有任何改动,就call callBack function,callback里面自定义干些事情,干什么都行。这样可以维护一个全局的callback数组,里面存被关照的path, 假如在create or create file时,查一下当前path是不是在callback array里面,如果在,就trigger callBack method after modification.

    Hashmap的做法

    class FileSystem {
        unordered_map<string, int> paths;
    public:
        FileSystem() {
            
        }
        
        bool createPath(string path, int value) {
             if (paths.count(path) > 0)
                return false; 
             string cur;
             for (int i = 0; i<path.size(); i++)
             {
                 if (i == 0 || path[i] != '/')
                 {
                     cur += path[i];
                 }
                 else if (i != 0 && path[i] == '/')
                 {
                     if (paths.count(cur) == 0)
                         return false;
                     else
                     {
                         cur += path[i];
                     }
                 }
             }
             paths[path] = value;
             return true;
        }
        
        int get(string path) {
            if (!paths.empty() && paths.count(path) > 0)
            {
                return paths[path];
            }
            return -1;
        }
    };
    


    Trie的做法
    struct TrieNode {
        unordered_map<string, TrieNode*> child;
        bool isFile;
        int content;
    
        TrieNode() {
            isFile = false;
            content = -1;
        }
    };
    
    class FileSystem {
    private:
        TrieNode* root = nullptr;
    
        vector<string> split(string& path) {
            vector<string> result;
            stringstream ss(path);
            string s;
            while(getline(ss, s, '/'))
            {
                if (s != "")
                {
                    result.push_back(s);
                }
            }
            return result;
        }
    
        bool insert(vector<string>& path, int value) {
            int n = path.size();
            TrieNode* cur = root;
            for (int i = 0; i < n; ++i) {
                if (cur -> child.find(path[i]) == cur -> child.end()) {
                    if (i != n - 1) {
                        return false;
                    }
                    cur -> child[path[i]] = new TrieNode();
                }
                else {
                    if (i == n - 1) {
                        return false; // the path already exist
                    }
                }
                cur = cur -> child[path[i]];
            }
            cur -> isFile = true;
            cur -> content = value;
            return true;
        }
    
        int find(vector<string>& path) {
            int n = path.size();
            TrieNode* cur = root;
            for (int i = 0; i < n; ++i) {
                if (cur -> child.find(path[i]) == cur -> child.end()) {
                    return -1;
                }
                cur = cur -> child[path[i]];
            }
            return cur -> content;
        }
    public:
        FileSystem() {
            root = new TrieNode();
        }
    
        bool createPath(string path, int value) {
            vector<string> p = split(path);
            return insert(p, value);
        }
    
        int get(string path) {
            vector<string> p = split(path);
            return find(p);
        }
    };
    

    Monday, February 10, 2020

    Leetcode @ Subsets, Permutations, Combination Sum, Coin Change 题目总结

    Subsets, Permutations, Combinations 此类题目都类似,都可以用recursion, backtracking来解,时间复杂度都很高,因为需要in depth search

    leetcode上的总结


    Let us first review the problems of Permutations / Combinations / Subsets, since they are quite similar to each other and there are some common strategies to solve them.
    First, their solution space is often quite large:
    • PermutationsN!.
    • CombinationsC_N^k = \frac{N!}{(N - k)! k!}
    • Subsets: 2^N, since each element could be absent or present.
    Given their exponential solution space, it is tricky to ensure that the generated solutions are complete and non-redundant. It is essential to have a clear and easy-to-reason strategy.
    There are generally three strategies to do it:
    • Recursion
    • Backtracking
    • Lexicographic generation based on the mapping between binary bitmasks and the corresponding
      permutations / combinations / subsets.
    As one would see later, the third method could be a good candidate for the interview because it simplifies the problem to the generation of binary numbers, therefore it is easy to implement and verify that no solution is missing.
    Besides, this method has the best time complexity, and as a bonus, it generates lexicographically sorted output for the sorted inputs.
    下面是具体的题目:

    Subsets

    Given a set of distinct integers, nums, return all possible subsets (the power set).
    Note: The solution set must not contain duplicate subsets.
    Example:
    Input: nums = [1,2,3]
    Output:
    [
      [3],
      [1],
      [2],
      [1,2,3],
      [1,3],
      [2,3],
      [1,2],
      []
    ]
    Accepted
    485,557
    Submissions
    838,363



    BackTracking


    class Solution {
    
    public:
        vector<vector<int>> subsets(vector<int>& nums) {
            vector<vector<int>> res;
            vector<int> cur;
            res.push_back(cur);
            
            for (int &n : nums)
            {
                vector<vector<int>> cur;
                for (auto r : res)
                {
                    r.push_back(n);
                    cur.push_back(r);
                }
                for (auto c : cur)
                {
                    res.push_back(c);
                }
            }
            return res;
        }
    };
    


    1. Combination Sum :没有重复的数组,找出所有unique combinations,数字可以重复利用。
    注意这里不需要sort,因为数字无重复。因为数字可以给重复利用,所以 i = index, 也就是自己可以继续add to sum,进入recursion,直到等于或者超过target,再退回来,去加下一个number,所以recursion的退出条件,sum == target, sum > target都很必要。

    class Solution {
        void helper(vector<int>& candidates, int target, vector<vector<int>> &res, vector<int> &cur, int index, int sum)
        {
            if (sum == target)
            {
                res.push_back(cur);
                return;
            }
            
            if (sum > target) return;
            
            for (int i = index; i < candidates.size(); i++)
            {
                cur.push_back(candidates[i]);
                helper(candidates, target, res, cur, i, sum + candidates[i]);
                cur.pop_back();
            }
            return;
        }
    public:
        vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
            vector<vector<int>> res;
            vector<int> cur;
            helper(candidates, target, res, cur, 0, 0);
            return res;
        }
    };
    


    2. Combination Sum II: 和combination sum有很大的不同,首先数组里面有重复数字,但数字不可重复利用,同时返回的结果要是unique的。因为有重复数字,那么就可能有重复结果,只是顺序变了。如何处理?

    这个地方破题的关键点在于,如果有多个重复的数字,产生单个结果时我可以用上它们,但是呢,在用它们来产生全局结果时,很可能产生duplicate的结果。

    此时,我们可以先sort数组,然后用一个判断条件 if (i > index && candiates[i] == candidates[i-1]) continue; 这句是关键。i > index,  只可能发生在i == index 的recursion已经执行完了,for循环第二层开始了,如果此时当前元素和前一个元素相等, 需要跳过,因为,前面那个同样值的candidate已经产生了所有的含有candidate的结果,不需要再来一遍了。

    由于值不能重复利用,在下一个recursion时,要i + 1. 如果可以重复利用,就直接 i 就行了。

    class Solution {
        void helper(vector<int>& candidates, int target, vector<vector<int>> &res, vector<int> &cur, int index, int sum)
        {
            if (sum == target)
            {
                res.push_back(cur);   
                return;
            }
            
            if (sum > target) return;
            
            for (int i = index; i < candidates.size(); i++)
            {
                if (i > index && candidates[i] == candidates[i-1]) continue;
                cur.push_back(candidates[i]);
                helper(candidates, target, res, cur, i+1, sum + candidates[i]);
                cur.pop_back();
            }
            return;
        }
    public:
        vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
            vector<vector<int>> res;
            vector<int> cur;
            sort(candidates.begin(), candidates.end());
            helper(candidates, target, res, cur, 0, 0);
            return res;
        }
    };
    


    3. Combination Sum III: given k numbers that add up to number n, numbers from 1 to 9 can be used. Each combination should be an unique set. 

    熟练掌握了I,II后,III就很简单了。相当于{1...9} 的数组,数字不能重复这样的case,只是加了一个限定条件就是结果的size。

    class Solution {
        void helper(vector<int> cand, vector<vector<int>> &res, vector<int> &cur, int k, int n, int index, int sum)
        {
            if (cur.size() == k && sum == n)
            {
                res.push_back(cur);
                return;
            }
            
            if (cur.size() > k || sum > n) return;
            
            for (int i = index; i<cand.size(); i++)
            {
                cur.push_back(cand[i]);
                helper(cand, res, cur, k, n, i+1, sum + cand[i]);
                cur.pop_back();    
            }
        }
    public:
        vector<vector<int>> combinationSum3(int k, int n) {
            vector<int> cand;
            for (int i = 1; i<=9; i++)
            {
                cand.push_back(i);
            }
            vector<vector<int>> res;
            vector<int> cur;
            helper(cand, res, cur, k, n, 0, 0);
            return res;
        }
    };
    




    4. Combination IV: 给一个正数没有重复数的数组,找出所有可能的combinations的和是一个给定正数,数字可以重复使用, 和I不同的是,这里的order matters, 也就说两个结果,数字相同,但是顺序不同,也算不同的结果,更像permutation sum instead of combination sum。以及这里只需要返回possible number of result就行。follow up: 如果数组里面有负数呢?

    这里用的是DP而非recursion。得想想这里怎么做的。

    class Solution {
    public:
        int combinationSum4(vector<int>& candidates, int target) {
            vector<int> dp(target + 1);
            dp[0] = 1;
            sort (candidates.begin(), candidates.end());
            for (int i = 1; i <= target; i++) {
                for (auto num : candidates) {
                    if (i < num) break;
                    dp[i] += dp[i - num];
                }
            }
            return dp.back();
        }
    };
    


    有人的总结 https://leetcode.com/problems/combination-sum-iv/discuss/85120/C%2B%2B-template-for-ALL-Combination-Problem-Set

    5. Coin Change

    给一堆硬币,和一个值,找到最少硬币 == 值的硬币个数。看上去和combination sum有点类似,brute force 可以把所有 combination sum都找出来,然后个数最少的返回,不过会超时。既然有最少,那么一看就是DP的感觉,所以看哪些是可重复的计算值,并且思考推导公式。

    举例, f(1) = 1, f(2) = 1, f(5) = 1. f(amount) = min(f(amount - coins[i])) + 1
    再想想code咋写。得到了推导式,但是怎么一步一步得到f(amount)呢?这里我卡住了。

    这里的思路是,针对每一个value, 按照value - coin 的思路循环一次,计算所有已经有最小值的dp[i - coin]。要满足条件就是, i - coin >= 0, dp[i - coin] != -1, dp[i - coin ] < min.

    class Solution {
    public:
        int coinChange(vector<int>& coins, int amount) {
            if (amount == 0) return 0;
            int dp [amount+1] = {-1};
            dp[0] = 0;
            for (int i = 0; i<coins.size() && coins[i] <= amount; i++)
            {
                dp[coins[i]] = 1;
            }
            for (int i = 1; i<=amount; i++)
            {
                int min = INT_MAX;
                for (int &coin : coins)
                {
                    if (i - coin >= 0 && dp[i-coin] != -1 && min > dp[i-coin])
                    {
                        min = dp[i-coin] + 1;
                    }
                }
                dp[i] = min != INT_MAX? min : -1;
            }
            return dp[amount];
        }
    };
    

    6. Letter Combinations of a phone number
    电话号码排列组合,不难,先把相应数字对应的string给弄出来。再搞两个for loop搞定,反正顺序是一定的。
    class Solution {
        void helper(vector<string> cand, vector<string> &res, string &cur, int index)
        {
            if (cand.empty() || cur.size() > cand.size()) return;
            
            if (cur.size() == cand.size())
            {
                res.push_back(cur);
                return;
            }
            for (int i = index; i<cand.size(); i++)
            {
                for (int j = 0; j<cand[i].size(); j++)
                {
                    cur.push_back(cand[i][j]);
                    helper(cand, res, cur, i+1);
                    cur.pop_back();
                }
            }
        }
    public:
        vector<string> letterCombinations(string digits) {
            string rec [] = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
            vector<string> cand;
            for (auto &c : digits)
            {
                cand.push_back(rec[c-'0'-2]);
            }
            vector<string> res;
            string cur;
            helper(cand, res, cur, 0);
            return res;
        }
    };

    Leetcode 316. Remove Duplicate Letters

     这道题表面问的是如何删除重复,实际在问如何从多个字符选取一个保留,从而让整个字符串按升序排列。那么策略就是对于高顺位的字符比如‘a',就要选靠前位置的保留,而低顺位字符如’z'则应该尽量选取靠后位置保留。 算法大概思路:每看到一个字符,我们要决定是否保留 1. ...