Yelp Onsite 1-10
https://qiuzhihui.gitbooks.io/r-book/content/yp_design.html
P1 How do I find the greatest sum on a path from root to leaf (that can only touch one node per level—no going up and down) in this tree like structure?Question Quora link
class Solution {
public:
int maxPathSum(TreeNode* root) {
if (!root) return 0;
int l = maxPathSum(root->left);
int r = maxPathSum(root->right);
return max(l, r)+root->val;
}
};
这道题抽空写一下吧 124. Binary Tree Maximum Path Sum
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
int helper(TreeNode* root, int& res) {
if (!root) return 0;
int l = helper(root->left, res); l = l*(l>0);
int r = helper(root->right, res); r = r*(r>0);
res = max(res, l+r+root->val);
return max(l, r)+root->val;
}
public:
int maxPathSum(TreeNode* root) {
int res = INT_MIN;
helper(root, res);
return res;
}
};
P2 第一轮面,上来就问 why yelp,然后问简历的project。完了之后做题目,leetcode 121 best time to buy and sell stock 原题。我一上来居然想到了trapping water那题。。。用了两个数组来存某个位置其左边最小值,和其右边最大值。在面试官引导优化了空间,最后其实不用额外空间就可以的。。。
121. Best Time to Buy and Sell Stock
class Solution {
public:
int maxProfit(vector<int>& prices) {
int low = INT_MAX, res = 0;
for (int& p:prices) {
low = min(low, p);
res = max(res, p-low);
}
return res;
}
};
P3 第二轮面,还是问 why yelp,然后问简历的project。然后是一道 smallest k elements in an array的题,用个max heap搞定。我感觉挺满意。结果面试官又来一题。简易版的 lc 290 word pattern, 比如 foo 对应 app 就是对的,要是 foo 对应 apg 那就是错啦。很简单吧。结果我上来只用了一个HashMap,写完了后,他让我写test case,我自己写了下,发现不对,赶紧又加了一个HashMap.. 险过。
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
class Solution {
public:
vector<int> kthSmall(vector<int> nums, int k) {
priority_queue<int> pq;
for (int a: nums) {
if (pq.size() < k) pq.push(a);
else if (pq.top() > a){
pq.pop(); pq.push(a);
}
}
vector<int> res;
while (!pq.empty()) { res.push_back(pq.top()); pq.pop(); }
return res;
}
};
int main() {
Solution sol;
for (auto s: sol.kthSmall({10,9,8,7,6,5,4,3,2,1}, 3)) cout << s << " ";
cout << endl;
}
class Solution {
public:
bool wordPattern(string pattern, string str) {
istringstream is(str);
string chunk; int i = 0;
unordered_set<string> used;
unordered_map<char, string> m;
while (getline(is, chunk, ' ')) {
if (!m.count(pattern[i])) {
if (used.count(chunk)) return false;
used.insert(chunk);
m[pattern[i]] = chunk;
} else if (m[pattern[i]] != chunk) return false;
++i;
}
return i == pattern.size();
}
};
P4 第三轮面,还是问 why yelp,然后问简历的project。需要提一下,每轮面试官结束后,他会和下个面试官交流个两三分钟,然后下个面试官再进来。我前两轮介绍的project都是我最熟的那个,结果第三个面试官就直接问我,你咋总是讲那一个project。。。 好吧,换个project开始侃了。侃完了后,出一道题,懵了。
你们帮我看一看。题目是:给一串movie,假定每个movie都是一个小时,并且都在整点播放;给你一个List的movies,让你安排播放时间,如果没有答案throw一个exception。
比如
电影A: 14, 15, 16, 17
电影B: 14, 15, 16
电影C: 14, 15
电影D: 14, 15, 17
返回一个解就行,比如 A 14, B 16, C 15, D 17。 如果你要 A 14, B 15, 后面 C就没法看了。
发现了没,全是地里面yelp出现过的原题。要好好看面经啊,也要多来发面经啊,大家要团结。算法题都是讲完了思路,然后写白板code的,写完后带着对方walk through一下。讲思路的时候可以乱一点,先来个brute force也行。但开始写的时候就不能乱来了,要尽量别出错。哎,我平时题目刷的不不够熟,表现的一般吧。还跪了第三轮。。 面我的大多是做backend的,我正好最近在研究谷歌三剑客,把GFS, MapReduce和他们讲了一通,感觉还好。 面试结束后HR有和我聊了一阵,然后说负责我的HR没来,下周二她回来上班,然后如果positive的话,会问我要reference。不知道跪没跪。。哎,求人品~~~
#include <vector>
#include <unordered_set>
#include <iostream>
using namespace std;
class Solution {
void dfs(vector<vector<int>>& movies, vector<pair<int,int>>& out, int i, unordered_set<int> s, vector<vector<pair<int,int>>>& res) {
if (i == movies.size()) { res.push_back(out); return; }
for (int mt: movies[i]) { // movie time of current movie i
if (!s.count(mt)) {
out.push_back({i, mt});
s.insert(mt);
dfs(movies, out, i+1, s, res);
out.pop_back();
s.erase(mt);
}
}
}
public:
vector<vector<pair<int,int>>> movieSchedule(vector<vector<int>>& movies) {
vector<vector<pair<int,int>>> res;
vector<pair<int,int>> out;
unordered_set<int> s;
dfs(movies, out, 0, s, res);
return res;
}
};
int main() {
Solution sol;
vector<vector<int>> movies = {{14,15,16,17}, {14,15,16}, {14,15}, {14,15,17}};
for (auto& s: sol.movieSchedule(movies)) {
for (auto& m: s) {
cout << m.first << " " << m.second << ",";
}
cout << endl;
}
}
P5 还是问 why yelp,然后问还有什么feature可以加到yelp里。那好多feature啊,你yelp不能买票吧,你yelp不能叫外卖吧,你yelp不能直接付款吧,你yelp不能团购吧,你yelp没有饭店哭胖吧。。。
然后上题,设计一个Rate limitor。就是一小时内访问第六次时,就返回false。这也没啥算法啊,我没太明白,就是HashMap,存访问的IP和ArrayList,然后同一个IP一小时内访问第六次,那就返回 false呗。面试官说就是这样的,然后我用java把这个函数写出来。然后讨论了些他做的工作啊什么的,结束了
跟359. Logger Rate Limiter有点像
#include <deque>
#include <unordered_map>
#include <iostream>
using namespace std;
class RateLimiter {
public:
RateLimiter(int x): cap(x) {}
bool checkIP(int ip, int timestamp) {
if (m[ip].size() < cap) {
m[ip].push_back(timestamp);
return true;
} else {
if (timestamp - m[ip].front() >= 60) {
m[ip].pop_front();
m[ip].push_back(timestamp);
return true;
}
return false;
}
}
private:
int cap;
unordered_map<int, deque<int>> m;
};
int main() {
RateLimiter r(6);
cout << r.checkIP(1,1) << endl;
cout << r.checkIP(1,2) << endl;
cout << r.checkIP(1,3) << endl;
cout << r.checkIP(1,4) << endl;
cout << r.checkIP(1,5) << endl;
cout << r.checkIP(1,6) << endl;
cout << r.checkIP(1,7) << endl;
cout << r.checkIP(1,61) << endl;
cout << r.checkIP(1,62) << endl;
}
P6 1轮skype的美国大哥:聊简历,coding题是给你一群nodes,每一个node里有3个invariants: unique的id,可能重名的label,其parent的id。要求将一组乱序的nodes按照如下方式print出来:
Node A
Node B
Node C
Node D
Node E
这里BD的parent为A,C的为B,AE的为一个id=0的默认root(不print),也就是子node按照depth要缩进相应的空格。 这题不难,我是先遍历nodes重建了储存children关系的hash table,然后写了一个基于stack的方法,中间卡了下壳,还好大哥人很nice,提示了下,就“looks great to me”了。Follow up是“我看你写的是iteration,告诉我写成recursion的话pros和cons是啥”,我答了一些基本比较,他满意,后面就闲聊
思路:我觉得这道题还是有点意思的啊。
#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>
using namespace std;
struct Node {
Node(int id_, int pid_, string label_): id(id_), pid(pid_), label(label_) {}
int id;
int pid;
string label;
};
class Solution {
void dfs(unordered_map<int, vector<Node*>>& m, Node* root, int depth) {
string indent(depth, ' ');
cout << indent << root->label << endl;
for (auto& a: m[root->id]) {
dfs(m, a, depth+1);
}
}
public:
void printNodes(vector<Node*>& nodes) {
unordered_map<int, vector<Node*>> m;
for (auto& n: nodes) {
m[n->pid].push_back(n);
}
for (auto& a: m[0]) {
dfs(m, a, 0);
}
}
};
int main() {
Node* a = new Node(1, 0, "A");
Node* b = new Node(2, 1, "B");
Node* c = new Node(3, 2, "C");
Node* d = new Node(4, 1, "D");
Node* e = new Node(5, 0, "E");
vector<Node*> nodes({a,b,c,d,e});
Solution s;
s.printNodes(nodes);
}
用stack的写法
#include <vector>
#include <string>
#include <unordered_map>
#include <stack>
#include <iostream>
using namespace std;
struct Node {
Node(int id_, int pid_, string label_): id(id_), pid(pid_), label(label_) {}
int id;
int pid;
string label;
};
class Solution {
public:
void printNodes(vector<Node*>& nodes) {
unordered_map<int, vector<Node*>> m;
stack<pair<Node*,int>> s;
for (auto& n: nodes) {
if (n->pid == 0) s.push({n,0});
else m[n->pid].push_back(n);
}
while (!s.empty()) {
auto t = s.top(); s.pop();
cout << string(t.second, ' ') << t.first->label << endl;
for (auto& a: m[t.first->id]) {
s.push({a, t.second+1});
}
}
}
};
int main() {
Node* a = new Node(1, 0, "A");
Node* b = new Node(2, 1, "B");
Node* c = new Node(3, 1, "C");
Node* d = new Node(4, 0, "D");
vector<Node*> nodes({a,b,c,d});
Solution s;
s.printNodes(nodes);
}
P7 2轮中国大哥:一个很典型的干瘦中国人,搞deep learning的,问题很尖锐。聊简历:问的很深,HMM,GMM,CNN,EM等。设计题:打开yelp,输入关键词restaurant返回一个附近restaurant的候选列表,问这一功能如何实现?答的search+filter+ranking,后来问我为何你用这种ranking方法不用其他的,这应该是一个开放性问题。coding:设计一个函数,给定一个list,能够返回一个内部元素random permutation过后的该list。很快写出一共不到10行,随后问一些基于permutation的概率题,最后说你如何测试该函数。楼主答的一测试edge cases,二测试overall results是否uniformly distributed。由于并不是很懂伪随机的原理,可能在这里答一些伪随机的内容会更好。
#include <vector>
#include <map>
#include <iostream>
#include <algorithm>
#include <cstdlib>
using namespace std;
class Solution {
public:
void randomPermute(vector<int>& nums) {
int n = nums.size();
for (int i = n-1; i >= 0; --i) {
int j = rand() % (i+1);
swap(nums[i], nums[j]);
}
}
};
int main() {
vector<int> nums({1, 2, 3});
Solution s;
s.randomPermute(nums);
for (auto& a: nums) cout << a << " "; cout << endl;
map<vector<int>, int> m;
for (int i = 0; i < 1000; ++i) {
s.randomPermute(nums);
++m[nums];
}
for (auto& a: m) {
for (auto& b: a.first) cout << b << " ";
cout << "," << a.second << endl;
}
}
P8 3轮美国小哥:Physics PhD帅哥。聊简历,设计题:yelp现在要给附近一些新发掘的店铺定制category label,问你如何根据user对该新店铺的review得知该店铺属于restaurant, gas station,...的哪一个。classification问题,也是开放性的。Followup是如何对应“I just had a meal nextdoor”这种容易将一般店铺误判为restaurant的review。coding题出乎意料的简单。说是yelp每次有人访问会给一个临时id,6位digits,问你如何判断这6数字里有重复的…一行搞定,第二问是给了一个当天所有临时id的log,求今天id中带有重复数字的比率是多少…套用前问结果10行搞定,最后又来permutation相关的概率题,但都很简单。
#include <vector>
#include <unordered_set>
#include <iostream>
using namespace std;
class Solution {
public:
bool isDuplicate(int id) {
if (id <= 10) return true;
unordered_set<int> s;
int len = 0;
while (id) {
s.insert(id%10);
id /= 10;
++len;
}
return len == (int)s.size();
}
double percent(vector<int>& ids) {
int cnt = 0;
for (auto id: ids) {
if (isDuplicate(id)) ++cnt;
}
return 1.0*cnt/ids.size();
}
};
int main() {
Solution s;
vector<int> ids({1,2,3,4,5,6,7,8,9,10,11,100});
cout << s.percent(ids) << endl;
}
P9 4轮三哥:不好好听就提问打断,玩手机,全程站起来转椅子玩(醉了),不交流不帮助不给思考时间,楼主卡壳他就直接下一题。聊简历,coding题是leetcode原题longest palindromic substring,还问我之前见过这题没。我说没,然后想不起来O(n)的方法了就只好写了最土的从每个字符往两边搜的$$O(n^2)$$方法。Followup是我只有一个只能获得最长odd length palindrome的函数f_odd
,问如何调用这个得到原函数,也就是 f (s) = g1(f_odd(g2(s)))
,求g1,g2。楼主答了给原string插入#
再处理,最后得到结果后每隔一个字去除#
,被问原string含#
怎么办,卡壳不到30秒就说算了我问你behavioral question,然后心不在焉地听了会就结束了。
class Solution {
public:
string longestPalindrome(string s) {
if (s.size() < 2) return s;
int l = 0, r = 0, n = s.size(), maxlen = 0, maxstart = 0;
for (int i = 0; i < n && n-i > maxlen/2;) {
l = r = i;
while (r < n-1 && s[r] == s[r+1]) ++r; // skip duplicates
i = r+1;
while (r < n-1 && l > 0 && s[l-1] == s[r+1]) {
++r; --l;
}
int len = r-l+1;
if (len > maxlen) {
maxlen = len;
maxstart = l;
}
}
return s.substr(maxstart, maxlen);
}
};
Manacher's Algorithm 6ms
class Solution {
string preProcess(string& s) {
string t("#");
for (auto c: s) {
t += c; t += "#";
}
return t;
}
public:
string longestPalindrome(string s) {
string t = preProcess(s);
// cout << t << endl;
int n = t.size(), C = 0, R = 0, mC = 0, mlen = 0;
vector<int> P(n, 0);
for (int i = 1; i < n-1; ++i) {
int i_mirror = 2*C-i; // equals to i' = C - (i-C)
P[i] = (R > i) ? min(P[i_mirror], R-i):0;
while (i-P[i]-1 >= 0 && i+P[i]+1 < n && t[i-P[i]-1] == t[i+P[i]+1]) ++P[i];
//cout << t[i] << " " << P[i] << endl;
if (i+P[i] > R) {
C = i; R = i+P[i];
}
if (mlen < P[i]) {
mC = i; mlen = P[i];
}
}
return s.substr((mC-mlen)/2, mlen);
}
};
哈哈谢谢clarification. 如果这样,好像并不需要考虑原str是否含#啊? 因为对插入处理后str调用f_odd
得到的最长子串substr_n
,总是以#开头并且这个#一定是后来插入的(反证法)。去除idx = 0, 2, 4。。。上的#就可以了吧?
P10 TinyURL 用最简单的发号的方法写了一个
#include <string>
#include <unordered_map>
#include <exception>
#include <iostream>
using namespace std;
class TinyUrl {
public:
string longToShort(string url) {
if (l2s.count(url)) return prefix+to_string(l2s[url]);
int cnt = l2s.size();
l2s[url] = cnt;
s2l[cnt] = url;
return prefix+to_string(cnt);
}
string shortToLong(string url) {
if (url.substr(0,8) == prefix) {
int cnt = stoi(url.substr(8));
if (s2l.count(cnt)) return s2l[cnt];
throw exception();
}
throw exception();
}
private:
string prefix = "zach.cn/";
unordered_map<string, int> l2s;
unordered_map<int, string> s2l;
};
int main() {
TinyUrl so;
cout << so.longToShort("www.abc.com/sadjasnvxvx") << endl;
cout << so.shortToLong("zach.cn/0") << endl;
cout << so.longToShort("www.aaa.com/dsakhzxhvcx") << endl;
cout << so.shortToLong("zach.cn/1") << endl;
cout << so.longToShort("www.bbb.comwqruwqifqin") << endl;
cout << so.shortToLong("hihi") << endl;
}
P10 第二个是个印度manager,聊projects,让写merge interval,问了dns, tcp, 数据库等基本知识。
class Solution {
public:
vector<Interval> merge(vector<Interval>& intervals) {
if (intervals.empty()) return {};
sort(intervals.begin(), intervals.end(), [](Interval&a, Interval&b){return a.start < b.start;});
vector<Interval> res = {intervals[0]};
for (int i = 1; i < intervals.size(); ++i) {
if (res.back().end < intervals[i].start) res.push_back(intervals[i]);
else {
res.back().end = max(intervals[i].end, res.back().end);
}
}
return res;
}
};