2019-10-31 14:22:30 +05:30

479 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<string, vector<pair<int,string>>> mp;
TimeMap() {
}
void set(string key, string value, int timestamp) {
if(mp.find(key)!= mp.end()){
mp[key].push_back({timestamp,value});
}else{
vector<pair<int,string>> 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 personss 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<int> &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<int>&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<int> Solution::order(vector<int> &A, vector<int> &B) {
int n = A.size();
vector<int> tree(4*n);
vector<pair<int,int>> person;
for(int i = 0; i<n; i++){
person.push_back({A[i],B[i]});
}
sort(person.begin(), person.end());
buildST(0,n-1,0,tree);
vector<int> 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<Interval> Solution::merge(vector<Interval> &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<Interval> 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<string> &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+1<s.size())? s.substr(i+1, s.size()-i-1):"";
makevalid(newS,open_to_remove, close_to_remove-1, open_count, close_count, i, res);
}
if(s[i] == '(' && open_to_remove>0){
string newS = s.substr(0,i);
newS += (i+1<s.size())? s.substr(i+1, s.size()-i-1):"";
makevalid(newS,open_to_remove-1, close_to_remove , open_count, close_count, i, res);
}
if(s[i] == ')') close_count++;
else if(s[i] == '(') open_count++;
}
}
}
int findMin(string s, int &open_to_remove, int &close_to_remove){
int ans = 0, count =0;
for(auto c:s){
if(c==')'){
count--;
}else if(c=='('){
count++;
}
if(count < 0){
ans++;
count=0;
}
}
open_to_remove = count;
close_to_remove = ans;
return ans+count;
}
vector<string> removeInvalidParentheses(string s) {
unordered_set<string> 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<string> 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| {"()"}|