# Google and Facebook Interviews ## Interview Tips 1. Always give Brute Force for every question. - Benefits: - It favours against worse odds. It is better to give a brute force then to be totally blank. - Predicts Optimal Solution 2. Data Structures/ Algorithms Time Complexity| Datastructure and Algorithms :--|:-- $O(n\log{n})$ | Sorting $O(n)$ | Two Pointers/ Smart Sorting/ dfs/ bfs $O(n^2)$ | Dynamic Programming $O(\log{n})$ | Binary Search $O(1)$ | HashMap 3. Writing a working code - Google/Facebook/Uber/Microsoft - They need working code - Take care of Edge cases - Candidates may get rejected for not taking care of Edge cases - Spend $10\%$ of your time on Edge cases - Give proper names to characters - Benefits - Less Confusion, Error free code - Good presentation and easier to explain ## Question 1 Time Based Key-Value Store Create a timebased key-value store class TimeMap, that supports two operations. 1. set(string key, string value, int timestamp) - Stores the key and value, along with the given timestamp. 2. get(string key, int timestamp) - Returns a value such that set(key, value, times tamp_prev) was called previously, with timestamp_prev <= timestamp. - If there are multiple such values, it returns the one with the largest timestamp_prev. - If there are no values, it returns the empty string (""). Example 1: ```C++ Input: inputs = ["TimeMap","set","get","get","set","get","get"], inputs = [[],["foo","bar",1],["foo",1],["foo",3], ["foo","bar2",4],["foo",4],["foo",5]] Output: [null,null,"bar","bar",null,"bar2","bar2"] Explanation: TimeMap kv; kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1 kv.get("foo", 1); // output "bar" kv.get("foo", 3); // output "bar" since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 ie "bar" kv.set("foo", "bar2", 4); kv.get("foo", 4); // output "bar2" kv.get("foo", 5); //output "bar2" ``` Example 2: ```C++ Input: inputs = ["TimeMap","set","set","get","get","get","get","get"], inputs = [[],["love","high",10],["love","low",20],["love",5],["love",10],["love",15],["love",20],["love",25]] Output: [null,null,null,"","high","high","low","low"] ``` __Note:__ - All key/value strings are lowercase. - All key/value strings have length in the range [1, 100] - The timestamps for all TimeMap.set operations are strictly increasing. - 1 <= timestamp <= 10^7 - TimeMap.set and TimeMap.get functions will be called a total of 120000 times (combined) per test case. __Brute Force__ - Store key, value and timestamp in an array. - For every get request run a linear loop to find the largest timestamp in the given bounds with the given key. Return value. - Time complexity: - TimeMap.set = $O(1)$. - TimeMap.get = $O(n)$. Because you are iterating over the whole array. __Hints__ - Suppose there were no time stamps. What would be the best data structure for this problem. Hashmap, As it gives $O(1)$ look up. - If we store all the timestamps corresponding to a key in an array, and store that array as value in a HashMap. Then for a given key we can run a loop on the corresponding array and find our answer. - This approach is still $O(n)$ as we may have only one key. - It is given that "The timestamps for all TimeMap.set operations are strictly increasing." Can we do a binary search to find our answer? - The time complexity now will be $O(\log{n})$. __Code__ ```C++ class TimeMap { public: /** Initialize your data structure here. */ unordered_map>> mp; TimeMap() { } void set(string key, string value, int timestamp) { if(mp.find(key)!= mp.end()){ mp[key].push_back({timestamp,value}); }else{ vector> temp; temp.push_back({timestamp,value}); mp[key] = temp; } } string get(string key, int timestamp) { if(mp.find(key) == mp.end()){ return NULL; } int lo = 0, hi = mp[key].size()-1; string ans; while(lo<=hi){ int mid = lo + (hi-lo)/2; if(mp[key][mid].first <= timestamp){ ans = mp[key][mid].second; lo = mid+1; }else{ hi = mid-1; } } return ans; } }; /** * Your TimeMap object will be instantiated and called as such: * TimeMap* obj = new TimeMap(); * obj->set(key,value,timestamp); * string param_2 = obj->get(key,timestamp); */ ``` __Time Complexity__ - Worst case it will $O(\log{n})$ for each get query, where n is the number of set queries. - For set query it will be $O(1)$. __Dry Run__ ```C++ TimeMap kv; kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1 kv.set("foo", "bar2", 2); kv.set("foo", "bar3", 3); kv.set("foo", "bar4", 4); kv.get("foo", 3); ``` |lo|hi|mid|key|mp[key]|Answer| |:--|:--|:--|:--|:--|:--| |0|3|1|foo|{{1,bar},{2,bar2},{3,bar3},{4,bar4}}|bar2| |2|3|2|foo|{{1,bar},{2,bar2},{3,bar3},{4,bar4}}|bar3| |3|3|3|foo|{{1,bar},{2,bar2},{3,bar3},{4,bar4}}|bar3| |3|2| |||| ## Question 2 Order of People Heights You are given the following : A positive number N Heights : A list of heights of N persons standing in a queue Infronts : A list of numbers corresponding to each person (P) that gives the number of persons who are taller than P and standing in front of P You need to return list of actual order of persons’s height Consider that heights will be unique Example ```C++ Input : Heights: 5 3 2 6 1 4 InFronts: 0 1 2 0 3 2 Output : actual order is: 5 3 2 1 6 4 Explaination: So, you can see that for the person with height 5, there is no one taller than him who is in front of him, and hence Infronts has 0 for him. For person with height 3, there is 1 person ( Height : 5 ) in front of him who is taller than him. You can do similar inference for other people in the list. ``` __Brute Force__ - Go through all the permutations of the heights and for every permutation check if it holds true with Infronts order. - The time complexity of this approach will be $O(n!* n)$ __Hints__ - If we sort the arrays based on heights, can we do something? ```C++ Heights: 1 2 3 4 5 6 Infront: 3 2 1 2 0 0 ``` - We know the largest element will always have $0$ infront value. No matter how many elements we put in front of largest element there will be no larger element than the largest element. - Lets put largest element at first place. ```C++ Ans: 6 ``` - Now for the next largest element we know the position to insert. ```C++ Ans: 5 6 Ans: 5 6 4 Ans: 5 3 6 4 Ans: 5 3 2 6 4 Ans: 5 3 2 1 6 4 ``` - Putting a smaller element in front a given element will not affect it. Thus if we move from the largest to smallest we can insert elements at their respective positions. - Time complexity: $O(n^2)$. As we are shifting all the elements while inserting a given element. This is similar to insertion sort. - Can we do better than this? - If we already know which place a given element should go, we would not have to move the elements. - For this we need to know the number of empty places infront a given position. - If we fill elements in sorted order of ascending values, at every step we will know what ever is coming next will be greater than the given element. - We can use segment trees to do the range queries. __Code__ ```C++ void buildST(int s, int e, int index, vector &tree){ if(s == e){ tree[index] = 1; return; } int mid = s + (e-s)/2; buildST(s, mid, index*2+1, tree); buildST(mid+1, e, index*2+2, tree); tree[index] = tree[index*2+1] + tree[index*2+2]; } int getPos(vector&tree, int s, int e, int index, int pos){ if(s==e){ tree[index]--; return s; } int mid = s+(e-s)/2; if(pos >= tree[index*2+1]){ tree[index]--; return getPos(tree, mid+1, e,index*2+2, pos-tree[index*2+1]); }else{ tree[index]--; return getPos(tree, s, mid, index*2+1, pos); } } vector Solution::order(vector &A, vector &B) { int n = A.size(); vector tree(4*n); vector> person; for(int i = 0; i res(n); for(auto p: person){ int pos = getPos(tree,0,n-1,0,p.second); //cout << pos << endl; res[pos] = p.first; } return res; } ``` __Time Complexity__ - For each element we will spend $O(\log{n})$ time to find the proper position. Total time will be $O(n\log{n})$. - Space Complexity will be $O(n)$ as we will be storing the elements in a segment tree. __Dry Run__ ```C++ Heights: 1 2 3 4 5 6 Infront: 3 2 1 2 0 0 ``` |Segment Tree|Height|Infront|Answer| |:--|:--|:--|:--| |{0,5,6}{0,2,3}{3,5,3}{0,1,2}{2,2,1}{3,4,2}{5,5,1}{0,0,1}{1,1,1}{}{}{3,3,1}{4,4,1}{}{}|1|3|{, , ,1, , }| |{0,5,5}{0,2,3}{3,5,2}{0,1,2}{2,2,1}{3,4,1}{5,5,1}{0,0,1}{1,1,1}{}{}{3,3,0}{4,4,1}{}{}|2|2|{ , ,2,1, , }| |{0,5,4}{0,2,2}{3,5,2}{0,1,2}{2,2,0}{3,4,1}{5,5,1}{0,0,1}{1,1,1}{}{}{3,3,0}{4,4,1}{}{}|3|1|{,3,2,1, , }| |{0,5,3}{0,2,1}{3,5,2}{0,1,1}{2,2,0}{3,4,1}{5,5,1}{0,0,1}{1,1,0}{}{}{3,3,0}{4,4,1}{}{}|4|2|{,3,2,1, ,4}| |{0,5,2}{0,2,1}{3,5,1}{0,1,1}{2,2,0}{3,4,1}{5,5,0}{0,0,1}{1,1,0}{}{}{3,3,0}{4,4,1}{}{}|5|0|{5,3,2,1, ,4}| |{0,5,1}{0,2,0}{3,5,1}{0,1,0}{2,2,0}{3,4,1}{5,5,0}{0,0,0}{1,1,0}{}{}{3,3,0}{4,4,1}{}{}|6|0|{5,3,2,1,6,4}| |{0,5,0}{0,2,0}{3,5,0}{0,1,0}{2,2,0}{3,4,0}{5,5,0}{0,0,0}{1,1,0}{}{}{3,3,0}{4,4,0}{}{}|||| ## Question 3 Merge Overlapping Intervals Given a collection of intervals, merge all overlapping intervals. For example: ```C++ Given [1,3],[2,6],[8,10],[15,18], return [1,6],[8,10],[15,18]. ``` Make sure the returned intervals are sorted. __Brute Force__ - If we iterate over all the intervals and for each interval try to find another interval that is overlapping. - When we find an overlapping pair we can merge them and again start our iterations. - Time Complexity: For finding a pair we may take $O(n^2)$ time. Every time we merge a pair we reduce the list by one. There can be at maximum $O(n)$ pairs. So, the overall time complexity will be $O(n^3)$. __Hint__ - If we sort the intervals based on the starting time, all the overlapping itervals will be adjacent to each other. - We can then merge the overlapping intervals in linear time. __Code__ ```C++ /** * Definition for an interval. * struct Interval { * int start; * int end; * Interval() : start(0), end(0) {} * Interval(int s, int e) : start(s), end(e) {} * }; */ bool compareIntervals(Interval a, Interval b){ return a.start < b.start; } vector Solution::merge(vector &A) { // Do not write main() function. // Do not read input, instead use the arguments to the function. // Do not print the output, instead return values as specified // Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details sort(A.begin(), A.end(), compareIntervals); vector res; int cur_start, cur_end; bool first = true; for(auto a:A){ if(first){ first = false; cur_start = a.start; cur_end = a.end; continue; } if(a.start <= cur_end){ cur_end = max(a.end,cur_end); }else{ Interval *i = new Interval(cur_start, cur_end); res.push_back(*i); cur_start = a.start; cur_end = a.end; } } Interval* i = new Interval(cur_start, cur_end); res.push_back(*i); return res; } ``` __Time Complexity__ - $O(n)$ __Dry Run__ ```C++ Array: [1,3],[2,6],[8,10],[15,18] ``` |cur_start|cur_end|cur_interval|Answer| |:--|:--|:--|:--| | | |[1,3]|{}| |1|3|[2,6]|{}| |1|6|[8,10]|{{1,6}}| |8|10|[15,18]|{{1,6},{8,10}}| |15|18| | {{1,6},{8,10},{15,18}}| ## Question 4 Remove Invalid Parentheses Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note: The input string may contain letters other than the parentheses ( and ). Example 1: ```C++ Input: "()())()" Output: ["()()()", "(())()"] ``` Example 2: ```C++ Input: "(a)())()" Output: ["(a)()()", "(a())()"] ``` Example 3: ```C++ Input: ")(" Output: [""] ``` __Brute Force__ - If we are given a string of parentheses, we can check if it is valid or not. We can use a stack or a counter to do so. This will be an $O(n)$ algorithm. - We can recursively solve this question by keeping track of number of characters removed. - We will first remove no character and check the string. Then we will remove one character to iteratively using backtracking. - We will check when ever the number of characters to remove becomes zero. - When we find a valid string we can add it to the result set. As there can be multiple calls to same string. - Whenever the result array becomes non-empty we will stop increasing the number of characters to be removed and return. __Hint__ - Questions like this do not have good algorithms. - I am sharing it so that you understand the importance of Brute force. - However, you can apply some pruning to considerably improve the actual running time. - I have used several ideas to improve time complexity. - Count the number of bad parentheses. And you know your minimum. - I also counted number of bad open parentheses and bad close parentheses saperately. - I counted number of open and close parentheses so far. __Code__ ```C++ class Solution { public: bool isValid(string s){ if(s=="")return true; int count = 0; for(auto c: s){ if(c == '('){ count++; }else if (c== ')'){ if(count <= 0) return false; count--; } } if(count!=0) return false; return true; } void makevalid(string s, int open_to_remove, int close_to_remove, int open_count, int close_count, int from, unordered_set &res){ if(close_count > open_count) return; int toDelete = open_to_remove + close_to_remove; if(toDelete > s.size() - from){ return; } if(toDelete == 0 && isValid(s)){ res.insert(s); }else{ for(int i = from; i<=s.size()-toDelete; i++){ if(s[i] == ')' && close_to_remove>0){ string newS = s.substr(0,i); newS += (i+10){ string newS = s.substr(0,i); newS += (i+1 removeInvalidParentheses(string s) { unordered_set res; int open_to_remove, close_to_remove; findMin(s, open_to_remove, close_to_remove); makevalid(s,open_to_remove, close_to_remove,0,0,0,res); vector res2; for(auto s: res){ res2.push_back(s); } return res2; } }; ``` __Time Complexity__ - $O(2^n)$ For every i, every element will either be removed or included. __Dry Run__ ```C++ s = "())" ``` |Main Call|Children Calls|res| |:--|:--|:--| |makevalid("())",1,0)|makevalid("))",0,0), makevalid("()",0,1), makevalid("()",0,2)| | |makevalid("))",0,0)| Invalid | | |makevalid("()",0,1)| Valid| {"()"}| |makevalid("()",0,2)| Valid| {"()"}|