Contest 91 (2018年10月24日,周三)
链接:https://leetcode.com/contest/weekly-contest-91/
模拟比赛情况记录:第一题柠檬摊的那题6分钟AC,然后是第二题树的距离K的结点那题比较久,大概写了30分钟,第三题翻转矩阵那题第一次提交错误了,列的优化方法思路错了,WA。后来比赛时间到了才改过来。最后一题最短子数组的和比K大的这题不会做。
【860】Lemonade Change (第一题)
一个柠檬摊,有一队人排队要买柠檬,一个5刀,一开始摊主没有零钱,买柠檬的人给的钱有5刀,10刀,20刀的,问摊主是否能找开钱。
题解:直接模拟。

1 class Solution { 2 public: 3 bool lemonadeChange(vector<int>& bills) { 4 const int n = bills.size(); 5 if (n == 0) { return true; } 6 map<int, int> money; 7 for (auto& bill : bills) { 8 if (bill == 5) { 9 money[5]++; 10 } else if (bill == 10) { 11 if (money.find(5) == money.end() || money[5] == 0) { 12 return false; 13 } 14 money[5]--, money[10]++; 15 } else if (bill == 20) { 16 if (money[10] >= 1 && money[5] >= 1) { 17 money[10]--; money[5]--; 18 } else if (money[5] >= 3) { 19 money[5] -= 3; 20 } else { 21 return false; 22 } 23 money[20]++; 24 } 25 } 26 return true; 27 } 28 };
【861】Score After Flipping Matrix (第三题)
给了一个0,1矩阵,我们每次能把每一行或者每一列的0,1做翻转,(0变成1,1变成0)。然后我们把每行的二进制数加起来,问最大和是多少。
题解:这个题目有点像二进制的竖式加法,对于每一行来说,高位为1数才大,所以我们比较翻转前和翻转后数的大小就行了。对于每一列来说,相当于同位相加,我们想要尽可能多的 1, 所以我们数 1 的个数,1 的个数比 0 的个数少就翻转。

1 //这个题目行的翻转相当于高低位的翻转,我们希望每行的数字最大,那么每一行代表的数字就要比它翻转以后的大,不然我们就要翻转。 2 //列的翻转相当于做竖式加法时候的同位相加,我们希望同一列的 1 尽可能的多,所以如果这一列 0 的数量比 1 的数量多了,我们就要翻转 01 . 3 class Solution { 4 public: 5 int matrixScore(vector<vector<int>>& A) { 6 const int n = A.size(), m = A[0].size(); 7 for (auto& row : A) { 8 row = getLargerRows(row); 9 } 10 11 for (int j = 0; j < m; ++j) { 12 vector<int> col(n); 13 for (int i = 0; i < n; ++i) { 14 col[i] = A[i][j]; 15 } 16 col = getLargerCols(col); 17 for (int i = 0; i < n; ++i) { 18 A[i][j] = col[i]; 19 } 20 } 21 int res = 0; 22 for (int i = 0; i < n; ++i) { 23 res += getDecimal(A[i]); 24 } 25 return res; 26 } 27 28 vector<int> getLargerRows(const vector<int>& ori) { 29 string strOri = "", strNew = ""; 30 for (auto& c : ori) { 31 strOri += to_string(c); 32 strNew += to_string(1 - c); 33 } 34 if (strOri >= strNew) { 35 return ori; 36 } 37 vector<int> neww(ori); 38 for (auto& c : neww) { 39 c = 1- c; 40 } 41 return neww; 42 } 43 44 45 vector<int> getLargerCols(vector<int>& ori) { 46 const int n = ori.size(); 47 int cnt1 = 0; 48 for (auto& ele : ori) { 49 if (ele) { ++cnt1; } 50 } 51 if (cnt1 >= n - cnt1) {return ori;} 52 for (auto& ele : ori) { 53 ele = 1 - ele; 54 } 55 return ori; 56 } 57 58 int getDecimal(const vector<int>& vec) { 59 int res = 0; 60 for (int i = 0; i < vec.size(); ++i) { 61 res = (res * 2) + vec[i]; 62 } 63 //printf ("%d ", res); 64 return res; 65 } 66 67 68 void print(vector<vector<int>>& mat) { 69 const int n = mat.size(), m = mat[0].size(); 70 for (int i = 0; i < n; ++i) { 71 for (int j = 0; j < m; ++j) { 72 printf("%d ", mat[i][j]); 73 } 74 printf(" "); 75 } 76 return; 77 } 78 };
【862】Shortest Subarray with Sum at Least K (monotonic queue,2018年10月25日学习)(第四题)
给了一个数组 A 有正有负和一个正整数K,我们要求最短的子数组长度使得子数组的和大于K。
题解:看到数组大小是 50000,所以肯定不能用 O(N^2) 的算法暴力枚举所有子数组。最近学习了单调队列这种数据结构(deque实现)。结果尝试写了,还是思路不对。哈哈。
看了答案,说我们可以先求A数组的前缀和P数组。然后用单调队列搞一下就行了。单调队列怎么搞,解释如下:
P [i] 代表 A数组前 i 个数的前缀和。 我们想要得到 P[y] - P[x] >= K ,(0 <= x < y ) 。我们的目标是优化 min (y - x) 。
通过观察, 我们可以发现 (1)单调队列中的P数组必须是递增的,原因我们可以用反证法,我们假设 x1 < x2 && P[x1] > P[x2] ,对于一个固定的 y, P[y] - P[x] >= K ,变换一下就变成 P [x] <= P[y] - K, 我们假设 P[x1] <= P[y] - K ,那么必然有 P [x2] < P[x1] < P[y] - K 。然而因为 x2 > x1 ,所以 x2 的答案可能比 x1 优秀。所以单调队列中的 P 数组必然是递增的。
(2)对于 一个 x 来说,假如我们已经找到了一个 P[y1] 跟它组成了一个区间子数组满足大于等于 K 的条件,那么这个 x 就可以从队列里面弹出去了,因为后面的 y2 即使满足条件也肯定不如当前的 y1 优秀。

1 //这题是单调队列 monotonic queue 2 class Solution { 3 public: 4 int shortestSubarray(vector<int>& A, int K) { 5 const int n = A.size(); 6 int ans = n + 1; 7 vector<int> summ (n + 1, 0); 8 for (int i = 1; i < n + 1; ++i) { 9 summ[i] = summ[i-1] + A[i-1]; 10 } 11 deque<int> dq; 12 for (int i = 0; i < n + 1; ++i) { 13 int y = summ[i]; 14 while (!dq.empty() && summ[dq.back()] > y) { 15 dq.pop_back(); 16 } 17 while (!dq.empty() && y - summ[dq.front()] >= K) { 18 ans = min(ans, i - dq.front()); 19 dq.pop_front(); 20 } 21 dq.push_back(i); 22 } 23 return ans == n + 1 ? -1 : ans; 24 } 25 };
【863】All Nodes Distance K in Binary Tree (第二题)
给了一棵二叉树和一个结点 target,求距离 target 长度为 K 的结点有哪些。
题解:结果可以是target的子孙或者target的父节点的其他儿子的子孙。我用了一个vector记录从根节点到target结点的路径,然后把路径上每个结点的其他儿子都找了一遍。

1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 11 class Solution { 12 public: 13 #define NONE 0 14 #define LEFT 1 15 #define RIGHT 2 16 17 vector<int> distanceK(TreeNode* root, TreeNode* target, int K) { 18 if (!root || !target) { return vector<int>(); } 19 if (K == 0) {ans = {target->val}; return ans;} 20 findPath(root, target); 21 22 for (int i = 0; i < path.size(); ++i) { 23 if (K - i < 0) { break; } 24 int dir = NONE; 25 if (i > 0) { 26 if (path[i-1] == path[i]->left) { 27 dir = RIGHT; 28 } else if (path[i-1] == path[i]->right) { 29 dir = LEFT; 30 } 31 } 32 findChild(path[i], 0, K - i, dir); 33 } 34 return ans; 35 } 36 37 void findChild(TreeNode* cur, int height, const int K, int dir) { 38 if (height == K) { 39 ans.push_back(cur->val); 40 return; 41 } 42 if (cur->left && dir != RIGHT) { 43 findChild(cur->left, height+1, K, NONE); 44 } 45 if (cur->right && dir != LEFT) { 46 findChild(cur->right, height+1, K, NONE); 47 } 48 return; 49 } 50 51 bool findPath(TreeNode* cur, TreeNode* target) { 52 if (!cur) {return false;} 53 if (cur == target) { 54 path.push_back(cur); 55 return true; 56 } 57 if (cur->left) { 58 if (findPath(cur->left, target)) { 59 path.push_back(cur); 60 return true; 61 } 62 } 63 if (cur->right) { 64 if (findPath(cur->right, target)) { 65 path.push_back(cur); 66 return true; 67 } 68 } 69 return false; 70 } 71 72 vector<int> ans; 73 vector<TreeNode*> path; 74 };
Contest 92 (2018年10月25日,周四,题号 864-867)
链接:https://leetcode.com/contest/weekly-contest-92/
比赛情况:第一题3分钟,第二题少于20分钟左右,然后从第三题就开始坑。第三题跟2014年的亚马逊的一个笔试题很像,就是求比N大的第一个回文数字,然后加上判断这个数是不是素数。第三题还没debug完毕,比赛就结束了。第四题没看。
【867】Transpose Matrix (第一题 2分)
题意就是把一个二维数组旋转90度,行变成列,列变成行。
题解:直接做。

1 class Solution { 2 public: 3 vector<vector<int>> transpose(vector<vector<int>>& A) { 4 const int n = A.size(), m = A[0].size(); 5 vector<vector<int>> mat(m, vector<int>(n, 0)); 6 for (int i = 0; i < m; ++i) { 7 for (int j = 0; j < n; ++j) { 8 mat[i][j] = A[j][i]; 9 } 10 } 11 return mat; 12 } 13 };
【865】Smallest Subtree with all the Deepest Nodes (第二题 5分)
一棵二叉树,找到它所有最深的子节点的最小公共祖先。(好像是lca????不确定,但是还是自己做出来了)
题解:先dfs这棵树,然后找到最大的height,dfs的同时记录所有的路径保存在数组里面。然后从路径的数组里面挑出和最大height长度一样的路径,在这些路径中找一个结点 i, 满足如果 i 不是最后一个结点,那么必然存在两条路径 path1[i+1] != path2[i+1]

1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode* subtreeWithAllDeepest(TreeNode* root) { 13 if (!root) {return root;} 14 vector<TreeNode*> temp; 15 getPath(root, temp, 0); 16 for (auto& p : path) { 17 if (p.size() == globalHeight) { 18 maxPath.push_back(p); 19 } 20 } 21 //找最后一个每条路径都一样的结点。 22 TreeNode* firstCommonNode = root; 23 for (int i = 1; i < globalHeight; ++i) { 24 TreeNode* node = maxPath[0][i]; 25 for (int j = 1; j < maxPath.size(); ++j) { 26 if (maxPath[j][i] != node) { 27 return firstCommonNode; 28 } 29 } 30 firstCommonNode = node; 31 } 32 return firstCommonNode; 33 34 } 35 //dfs,获取最大高度和所有路径。 36 void getPath(TreeNode* root, vector<TreeNode*>& temp, int height) { 37 if (!root) { 38 path.push_back(temp); 39 globalHeight = max(globalHeight, height); 40 return; 41 } 42 temp.push_back(root); 43 getPath(root->left, temp, height + 1); 44 getPath(root->right, temp, height + 1); 45 temp.pop_back(); 46 return; 47 } 48 49 vector<vector<TreeNode*>> path; 50 vector<vector<TreeNode*>> maxPath; 51 int globalHeight = 0; 52 };
【866】Prime Palindrome(第三题 7分)
题意很简单,就是给了一个数字N,要求大于等于N的第一个回文素数。
题解:这题其实一开始有两个思路,第一个思路是先找比N大的素数,然后再判断它是不是回文;第二个思路是先找比N大的回文数,然后判断它是不是素数。我是先找回文然后再判断是不是素数的。
这题有个标准小题就是如何求比N大的第一个素数。这个做法见:https://blog.csdn.net/cjllife/article/details/39938187
算法如图所示:
当 num 是一位数的时候,num 小于 9 就返回 num + 1, num 等于 9 返回 11。
然后我们来看 num 是个 n 位数的情况,(1)当 n 是个奇数的时候,num 的前半段我们定义为比较长的半段(half)。我们从中间位置开始往两边找,找到第一个两边不对称的位置,p1 为左边不对称的下标, p2为右边不对称的下标,如果 num[p1] < num[p2],或者都扫描完了发现两边完全对称,那么就把 前半段(half)+1。注意如果加一以后进位了从 x 位变成 x+1 位了要特殊判断,特殊值写一下就能找到规律。然后加一之后的数就是新的数的前半段,后半段按照前半段翻转即可。如果 num[p1] > num[p2] 的话,我们直接翻转前半段half就行了。(2).同理,如果 n 是偶数的时候,前半段和后半段完全长度相同。我们从中间位置开始往两边找,依旧找 p1, p2。剩下的同理(1)。
总之写的 if-else 比较多,要注意好好测试。

1 //先构造回文,再判断是不是素数,比 N 大的回文数怎么构造。 2 class Solution { 3 public: 4 int primePalindrome(int N) { 5 if (isPalindrome(N) && isPrime(N)) { 6 return N; 7 } 8 int palin = N; 9 do { 10 //printf("palin = %d ", palin); 11 palin = getNextGreaterPalindrome(palin); 12 13 } while (!isPrime(palin)); 14 return palin; 15 } 16 bool isPalindrome(int num) { 17 string strN = to_string(num); 18 int start = 0, end = strN.size()-1; 19 while (start < end) { 20 if (strN[start] != strN[end]) { 21 return false; 22 } 23 start++, end--; 24 } 25 return true; 26 } 27 bool isPrime(int num) { 28 if (num == 1) {return false;} 29 for (int i = 2; i <= sqrt(num); ++i) { 30 if (num % i == 0) { 31 return false; 32 } 33 } 34 return true; 35 } 36 int getNextGreaterPalindrome(int num) { 37 if (num <= 9) { 38 return num == 9 ? 11 : num + 1; 39 } 40 string strNum = to_string(num); 41 const int n = strNum.size(); 42 int halfIdx = (n - 1) / 2; //n = 3, halfIdx = 1; n = 4, halfIdx = 1; 43 string strHalf = "", strNew = ""; 44 if (n & 1) { //odd size 45 int p1 = halfIdx - 1, p2 = halfIdx + 1; 46 while (p1 >= 0 && p2 < n) { 47 if (strNum[p1] != strNum[p2]) { 48 break; 49 } 50 p1--, p2++; 51 } 52 if (p1 == -1 || strNum[p1] < strNum[p2]) { //increase by 1 53 strHalf = strNum.substr(0, halfIdx + 1); 54 int intHalf = atoi(strHalf.c_str()); 55 ++intHalf; 56 string strHalfNew = to_string(intHalf); 57 string t = strHalfNew.substr(0, strHalfNew.size()-1); 58 reverse(t.begin(), t.end()); 59 strNew = strHalfNew + t; 60 61 } else { 62 strHalf = strNum.substr(0, halfIdx); 63 string t = strHalf; 64 reverse(t.begin(), t.end()); 65 strNew = strHalf + strNum[halfIdx] + t; 66 } 67 } else { // even size 68 int p1 = halfIdx, p2 = halfIdx + 1; 69 while (p1 >= 0 && p2 < n) { 70 if (strNum[p1] != strNum[p2]) { 71 break; 72 } 73 p1--, p2++; 74 } 75 //printf("p1 = %d, p2 = %d ,strNum[p1] = %c, strNum[p2] = %c ", p1, p2, strNum[p1], strNum[p2]); 76 if (p1 == -1 || strNum[p1] < strNum[p2]) { //increase by 1 77 strHalf = strNum.substr(0, halfIdx + 1); 78 int intHalf = atoi(strHalf.c_str()); 79 ++intHalf; 80 string strHalfNew = to_string(intHalf); 81 string t = strHalfNew; 82 reverse(t.begin(), t.end()); 83 84 // 是否有进位。。。 85 if ((int)strHalfNew.size() != (int)strHalf.size()) { 86 strNew = strHalfNew + t.substr(1, (int)strHalfNew.size()-1); 87 } else { 88 strNew = strHalfNew + t; 89 } 90 } else { 91 strHalf = strNum.substr(0, halfIdx + 1); 92 string t = strHalf; 93 reverse(t.begin(), t.end()); 94 strNew = strHalf + t; 95 } 96 } 97 98 //string -> int 99 int res = atoi(strNew.c_str()); 100 return res; 101 } 102 103 104 };
【864】Shortest Path to Get All Keys (第四题 9分)
Contest 93 (2018年10月26日,周五,题号 868-871)
链接:https://leetcode.com/contest/weekly-contest-93/
比赛情况记录:前三题都不难,甚至于最后放弃的第四题我觉得我好像见过。(42min 3道题)
【868】Binary Gap (第一题 3分)
给了一个正整数 N,返回 N 的二进制表示中,两个连续 1 的最长距离, 如果没有两个连续的 1,就直接返回 0。 比如 N = 6, 二进制是 110, 那么就返回 1,比如 N = 5,二进制是 101,就返回 2。
题解:(我的解法)转换成二进制,然后直接用两个指针比较。时间复杂度是 O(N)。
然而,solution给了 O(logN) 的做法。区别在于我存的是二进制的表示,人家存的是二进制表示中 1 的下标(index)。最后甚至于可以用滚动数组优化,把存储的矩阵给搞没了。(厉害了。)
【869】Reordered Power of 2 (第二题 5分)
给了一个正整数 N, 我们可以把 N 的数字进行重新排列,搞出来一个新的 N (新的数不能以 0 开头),问包括 N 在内的 N 的所有排列能不能有一个数是2的幂。
题解:(我的解法)我先把 N 字符串化,然后用 next_permutation 生成下一个排列,next_permutation() 这个方法有个小点要注意,使用前先对数组排序,才能生成所有的排列(在这里WA了一次)。然后一开始我搞了一个set,里面存了所有长度为10位以及以下的所有的2的幂。用 next_permutaion 生成的数去 set 里面找。

1 class Solution { 2 public: 3 bool reorderedPowerOf2(int N) { 4 map<int, int> cnt; 5 int t = N; 6 while (t) { 7 int r = t % 10; 8 cnt[r]++; 9 t /= 10; 10 } 11 long long x = 1; 12 int size = to_string(N).size(); 13 while (to_string(x).size() < size) { 14 x*= 2; 15 } 16 while (to_string(x).size() == size) { 17 if (isSameWithN(x, cnt)) { 18 return true; 19 } 20 x *= 2; 21 } 22 return false; 23 } 24 bool isSameWithN(int x, map<int, int> cnt) { 25 while (x) { 26 int r = x % 10; 27 if (cnt[r] > 0) { 28 cnt[r]--; 29 } else { 30 return false; 31 } 32 x /= 10; 33 } 34 return true; 35 } 36 };
然而,solution里面还有更优秀的解法。看完优秀解法我觉得我就是一个智障。我们可以先生成一个 2^x 这个数,然后判断它和 N 是不是由相同的数字构成。(时间复杂度??how)
【870】Advantage Shuffle (第三题 7分)
给了两个长度一样的数组 A 和 B,如果 A[i] > B[i],那么说明 A[i] 比 B[i] 优秀。返回一个 A数组的排列,使得 A 比 B 优秀数最多。
题解:我是直接贪心了。就是先把 A 做个排序,然后对于 B 中的每个元素用 upper_bound 求 A 中第一个比B[i] 大的元素,然后把这个元素从A删除。剩下的 A 的元素填补空缺。
discuss中有个说法我更喜欢,就是说如果 A中有比 B[i] 大的元素,就应该使用第一个(最小的)比 B[i] 大的元素,如果没有比 B[i] 大的元素,就是用 A 中最小的元素。

1 class Solution { 2 public: 3 vector<int> advantageCount(vector<int>& A, vector<int>& B) { 4 const int n = A.size(); 5 sort(A.begin(), A.end()); 6 vector<int> ans(n, 0), copyA = A; 7 for (int i = 0; i < n; ++i) { 8 auto iter = upper_bound(A.begin(), A.end(), B[i]); 9 if (iter != A.end()) { 10 int idx = distance(A.begin(), iter); 11 ans[i] = *iter; 12 A.erase(iter); 13 } else { 14 ans[i] = -1; 15 } 16 } 17 auto iter = A.begin(); 18 for (int i = 0; i < n; ++i) { 19 if (ans[i] == -1) { 20 ans[i] = *iter; 21 ++iter; 22 } 23 } 24 return ans; 25 } 26 };
【871】Minimum Number of Refueling Stops (第四题 9分)
Contest 94 (2018年10月27日,周六,题号 872-875)
链接:https://leetcode.com/contest/weekly-contest-94
比赛情况记录:这场总体来比较简单,第一题是二叉树的遍历,第二题是模拟题(比赛的时候不知道哪里错了,没法AC),第三题是个二分答案,第四题我用dp做的,最后比赛完超时了不算提交。
【872】Leaf-Similar Trees (第一题 4分)
判断两棵二叉树叶子结点的序列是不是一样的。
题解:我是把两棵树的序列都求出来然后做比较的。

1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 bool leafSimilar(TreeNode* root1, TreeNode* root2) { 13 if (!root1 && !root2) { return true; } 14 if (!root1 || !root2) { return false; } 15 vector<int> leaf1, leaf2; 16 getLeafArray(root1, leaf1); 17 getLeafArray(root2, leaf2); 18 if (leaf1.size() != leaf2.size()) { 19 return false; 20 } 21 for (int i = 0; i < leaf1.size(); ++i) { 22 if (leaf1[i] != leaf2[i]) { return false; } 23 } 24 return true; 25 } 26 void getLeafArray(TreeNode* root, vector<int>& leaf) { 27 if (!root) { return; } 28 if (!root->left && !root->right) { 29 leaf.push_back(root->val); 30 return; 31 } 32 if (root->left) { 33 getLeafArray(root->left, leaf); 34 } 35 if (root->right) { 36 getLeafArray(root->right, leaf); 37 } 38 return; 39 } 40 };
【874】Walking Robot Simulation (第二题 4分)
一个机器人在无限大的地板上行走,给了一个 commands 数组,行走规则如下:
(1) commands[i] 等于 -2, 机器人向左转90度
(2) commands[i] 等于 -1, 机器人向右转90度
(3) commands[i] 属于 [1, 9] , 机器人向前走 commands[i] 步
要求返回机器人所有走过的点中距离原点欧式距离最大的点,返回最大欧式距离的平方。
题解:审题是多么的重要!!!!!这题我 WA 了好多遍都没想明白为啥,最后看了 solution 发现他是要求最大值!!!! 直接模拟,不说了。(让我哭一会儿)

1 class Solution { 2 public: 3 int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) { 4 for (auto& p : obstacles) { 5 stObs.insert(make_pair(p[0], p[1])); 6 } 7 pair<long long, long long> curPos(0, 0); 8 dir curDir = north; 9 10 vector<long long> simpleCommands; 11 int idx = 0; 12 while (idx < commands.size()) { 13 if (commands[idx] < 0) { 14 simpleCommands.push_back(commands[idx]); 15 ++idx; 16 } else { 17 long long step = 0; 18 while (idx < commands.size() && commands[idx] > 0) { 19 step += (long long)commands[idx]; 20 ++idx; 21 } 22 simpleCommands.push_back(step); 23 } 24 } 25 26 long long ans = 0; 27 for (auto& com : simpleCommands) { 28 if (com < 0) { 29 curDir = changeDir(curDir, com); 30 //printf("changeDir succ. curDir = %d ", curDir); 31 } else { 32 pair<long long, long long> newPos; 33 newPos = getNewPos(curPos, curDir, com); 34 //printf("getNewPos succ. dir = %d, curPos = (%d, %d), newPos = (%d, %d), len = %d ", 35 //curDir, curPos.first, curPos.second, newPos.first, newPos.second, com); 36 curPos = newPos; 37 ans = max(ans, curPos.first * curPos.first + curPos.second * curPos.second); 38 } 39 } 40 return (int)ans; 41 } 42 43 //朝向转换 44 enum dir { 45 north = 1, 46 east = 2, 47 south = 3, 48 west = 4, 49 }; 50 set<pair<int, int>> stObs; 51 dir changeDir(dir NowDir, int turn) { 52 if (NowDir == north) { 53 return turn == -1 ? east : west; 54 } else if (NowDir == east) { 55 return turn == -1 ? south : north; 56 } else if (NowDir == south) { 57 return turn == -1 ? west : east; 58 } else if (NowDir == west) { 59 return turn == -1 ? north : south; 60 } 61 } 62 pair<long long, long long> getNewPos(pair<long long, long long>& curPos, dir curDir, long long& len) { 63 //north curPos.first += len 64 //south curPos.first -= len 65 //east curPos.second += len 66 //west curPos.second -= len 67 //printf("begin getNewPos: curPos = (%d, %d), curDir = %d, len = %d ", curPos.first, curPos.second, curDir, len); 68 if (curDir == north) { 69 for (int s = 0; s < len; ++s) { 70 pair<long long, long long> newPos = curPos; 71 newPos.second++; 72 if (stObs.find(newPos) != stObs.end()) { 73 //printf("encounter an obs. pos(%d, %d) ", newPos.first, newPos.second); 74 break; 75 } 76 curPos = newPos; 77 } 78 } else if (curDir == south) { 79 for (int s = 0; s < len; ++s) { 80 pair<long long, long long> newPos = curPos; 81 newPos.second--; 82 if (stObs.find(newPos) != stObs.end()) { 83 //printf("encounter an obs. pos(%d, %d) ", newPos.first, newPos.second); 84 break; 85 } 86 curPos = newPos; 87 } 88 } else if (curDir == east) { 89 for (int s = 0; s < len; ++s) { 90 pair<long long, long long> newPos = curPos; 91 newPos.first++; 92 if (stObs.find(newPos) != stObs.end()) { 93 //printf("encounter an obs. pos(%d, %d) ", newPos.first, newPos.second); 94 break; 95 } 96 curPos = newPos; 97 } 98 } else if (curDir == west) { 99 for (int s = 0; s < len; ++s) { 100 pair<long long, long long> newPos = curPos; 101 newPos.first--; 102 if (stObs.find(newPos) != stObs.end()) { 103 //printf("encounter an obs. pos(%d, %d) ", newPos.first, newPos.second); 104 break; 105 } 106 curPos = newPos; 107 } 108 } 109 return curPos; 110 } 111 };
【875】Koko Eating Bananas (第三题 6分)(二分答案)
有 N 堆香蕉,每一堆里面有 piles[i] 个香蕉,Koko只能在守卫离开的时候吃香蕉,已知守卫要离开 H 小时,假设Koko吃香蕉的速度是 K 个香蕉每小时, 每个小时内,Koko 只能吃一堆香蕉(即使香蕉堆里面的香蕉不足 K 个,他也不能去别的香蕉堆里面吃),Koko想在 H 小时里面把所有的香蕉吃完,问 K 最小是多少。
题解:首先思考了一个特殊的case,就是当 一共有 H 堆香蕉 而守卫恰好离开 H 小时的时候,K 的最小值就是 max(piles[i])。因为他必须一个小时恰好吃一堆,才能把所有香蕉吃完。然后我们通过思考一下可以发现, 当 香蕉的堆数 N <H 的时候,K 的最小值也可以变小,因为时间变长了,koko可以 2个小时只吃一堆香蕉。所以 我们可以推测出 K 是有范围的, (1 <= K <= max(piles[i]))。
为了方便,我们先把 piles[] 排个序。然后用二分答案的方法求 K 的最小值。二分的条件就是 koko 能不能在当前时间内吃完所有香蕉。

1 class Solution { 2 public: 3 int minEatingSpeed(vector<int>& piles, int H) { 4 const int n = piles.size(); 5 sort(piles.begin(), piles.end()); 6 if (H == n) { 7 return piles.back(); 8 } 9 // 1 <= K <= piles[n-1]; 10 int left = 1, right = piles[n-1] + 1; 11 while (left < right) { 12 int mid = (left + right) / 2; 13 if (!canFinish(piles, H, mid)) { //吃不完 14 left = mid + 1; 15 } else { 16 right = mid; 17 } 18 } 19 int K = left; 20 return K; 21 } 22 bool canFinish(vector<int>& piles, int H, int K) { 23 int cntH = 0; 24 for (auto p : piles) { 25 cntH += p % K ? p / K + 1 : p / K; 26 } 27 return cntH <= H; 28 } 29 };
【873】Length of Longest Fibonacci Subsequence (第四题 6分)
这个题最后做出来了。。。
Contest 95(2018年10月29日,周一,题号 876-879)
链接:https://leetcode.com/contest/weekly-contest-95
比赛情况记录:做出了两道题,第三题TLE了,第四题似曾相识然而没思路。ranking: 1078/3750。
【876】Middle of the Linked List (第一题 3分)
给了一个链表,返回中间结点,如果是奇数长度的链表就返回中间那个,如果是偶数长度的链表就返回中间靠后的那个。
题解:two pointers 一个往前走一步,一个往前走两步。当走的快的结点走到 nullptr 的时候,走的慢的结点正好走到中间。一次 AC 了。

1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode* middleNode(ListNode* head) { 12 if (!head) {return head;} 13 ListNode *slow = head, *fast = head; 14 while (fast && fast->next) { 15 if (fast->next) { 16 fast = fast->next->next; 17 slow = slow->next; 18 } 19 } 20 return slow; 21 } 22 };
【877】Stone Game (第二题 5分)(这题类似的题也做过了,但是还是不会做,需要往回看)
有 N 堆 (N 是偶数) 石子排成一排,每堆石子有 piles[i] 个,有玩家 1 和玩家 2 。游戏每轮只能从这排石子的最左或者最右两边拿一堆,最后谁的石子总数(石子总数是奇数,所以不会平手)最多谁就是赢家。假设两个玩家都绝顶聪明(play optimally),玩家 1 先开始游戏,问玩家 1 最后能不能赢,玩家 1 能赢返回 true, 不然返回false。
题解:这题还是不会啊,要 go die 了啊。做过的类似的题目有 lc 486 Predict the Winner; 464 Can I Win; 这种模式还是掌握不了。(dp; Minimax)。也可以参考 《程序员代码面试指南》Chp4. P233
《程序员代码面试指南》原题(排成一条线的纸牌博弈问题):给定一个数组 arr,代表数值不同的纸牌排成一条线。玩家 A 和玩家 B 依次拿走每张纸牌,规定玩家 A 先拿,玩家 B 后拿,但是每个玩家每次只能拿走最左或者最右的纸牌,玩家 A 和玩家 B 都绝顶聪明。请返回最后获胜者的分数。
抄一遍题解:定义递归函数 f(i, j), 表示如果 arr[i, j] 这个排列上的纸牌(石子)被绝顶聪明的人先拿,最终能获得什么分数。定义递归函数 s(i, j),表示如果 arr[i,j] 这个排列上的纸牌(石子)被绝顶聪明的人后拿,最终能获得什么分数。
我们首先来分析 f(i, j),具体过程如下:(1)当 i == j (即 a[i..j] )上只剩一张纸牌。那么当然会被先拿纸牌的人拿走, 所以返回 arr[i]。 (2)如果 i != j,那么当前拿纸牌的人有两个选择,要么拿走 arr[i], 要么拿走 arr[j]。如果拿走了 arr[i],那么排列剩下了 arr[i+1 ..j],对当前的玩家来说,面对 arr[i+1 .. j] 排列的纸牌,他成了后拿的人,所以他后续能获得的分数是 s(i+1, j)。如果这个人拿走了 a[j],那么排列将剩下 arr[i .. j+1]。对当前的玩家来说,面对 arr[i .. j-1] 排列的纸牌,他成了后拿的人,所以后续他能获得的分数是 s(i, j+1)。作为绝顶聪明的人,必然会在两种决策中选择最优的。所以返回 max{arr[i]+s(i+1, j), arr[j]+s(i, j-1)}。
然后来分析 s(i, j),具体过程如下:(1) 如果 i == j (即 a[i..j]) 上只剩一张纸牌。作为后拿纸牌的人必然什么也的不到,返回 0。 (2) 如果 i != j,根据函数 s(i, j) 的定义,玩家的对手会先拿纸牌。对手要么拿走 arr[i],要么拿走 arr[j]。如果对手拿走 arr[i],那么排列将剩下 arr[i+1.. j],然后轮到玩家拿。如果对手拿走 arr[j],那么排列将剩下 arr[i.. j-1],然后轮到玩家拿。对手也是决定聪明的人,所以,必然会把最差的情况留给玩家。所以返回 min{f(i+1, j), f(i, j-1)}。
暴力递归代码如下 win1: 在暴力递归的方法中,递归函数一共有 N 层,并且是 f 和 s 交替出现的。
///等待补充中

1 class Solution { 2 public: 3 bool stoneGame(vector<int>& piles) { 4 const int n = piles.size(); 5 int summ = 0; 6 for (auto p : piles) { 7 summ += p; 8 } 9 int score = f(piles, 0, n-1); //玩家 1 的分数 10 int score2 = s(piles, 0, n-1); //玩家 2 的分数 11 return score + score > summ ? true : false; 12 } 13 int f(vector<int>& piles, int i, int j) { 14 if (i == j) { 15 return piles[i]; 16 } 17 return max(piles[i] + s(piles, i+1, j), piles[j] + s(piles, i, j-1)); 18 } 19 int s(vector<int>& piles, int i, int j) { 20 if (i == j) { 21 return 0; 22 } 23 return min(f(piles, i+1, j), f(piles, i, j-1)); 24 } 25 };
下面介绍 dp 方法,假如数组 arr 的长度为 N,生成两个大小为 N * N的矩阵 f 和 s,f[i][j] 表示 f(i,j) 的返回值,s[i][j] 返回 s(i, j) 的返回值。规定一下计算方向即可。时间复杂度是 O(N^2),空间复杂度是 O(N^2)。

1 class Solution { 2 public: 3 bool stoneGame(vector<int>& piles) { 4 const int n = piles.size(); 5 int summ = 0; 6 for (auto p : piles) { 7 summ += p; 8 } 9 vector<vector<int>> f(n, vector<int>(n, 0)); 10 vector<vector<int>> s = f; 11 for (int j = 0; j < n; ++j) { 12 f[j][j] = piles[j]; 13 for (int i = j - 1;i >= 0; --i) { 14 f[i][j] = max(piles[i] + s[i+1][j], piles[j] + s[i][j-1]); 15 s[i][j] = min(f[i+1][j], f[i][j-1]); 16 } 17 } 18 int score = f[0][n-1]; 19 return score + score > summ ? true : false; 20 } 21 };
【878】Nth Magical Number (第三题 7分)(数学题,求循环节)
A positive integer is magical if it is divisible by either A or B. Return the N-th magical number. Since the answer may be very large, return it modulo 10^9 + 7
.
Example 1:
Input: N = 1, A = 2, B = 3
Output: 2
Example 2:
Input: N = 4, A = 2, B = 3
Output: 6
Note:
1 <= N <= 10^9
2 <= A <= 40000
2 <= B <= 40000
题解:这道题我一开始想的是丑数那题,按照那个方法做了直接tle。因为 N 太大了,O(N)的算法肯定是tle了。然后看了solution,答案说先求 A, B 的 lcm, 我们发现一个 lcm 里面包含了这样 step 个数 (step = (L/A) + (L/B) - 1) ,然后求循环节 times = N / step ; r = N % lcm; 所求的第 N 个数就相当于 lcm走了 times,然后剩余 r 个。(然后你知道怎么搞了...)。然后暴力求剩下的 r 个的时候,注意求数的时候一定不要 取 MOD,不然要凉凉。

1 #define MOD 1000000007 2 class Solution { 3 public: 4 int nthMagicalNumber(int N, int A, int B) { 5 long long L = getLcm(A, B); 6 long long step = (L / A) + (L / B) - 1; 7 long long times = N / step; 8 long long r = N % step; 9 long long base = (times * L) % MOD; 10 long long ans = 0; 11 if (r == 0) { 12 ans = base; 13 } else { 14 long long numA = A, numB = B; 15 int cnt = 0; 16 long long number = 0; 17 while (cnt < r) { 18 if (numA <= numB) { 19 number = numA; 20 //这里一定不要取 mod,当有一个数取了mod之后变成一个很小的数,后面就没法继续比较了,它只会一直加这个比较小的数 21 numA += A; //numA = (numA + A) % MOD; 22 } else if (numA > numB) { 23 number = numB; 24 //这里一定不要取 mod,当有一个数取了mod之后变成一个很小的数,后面就没法继续比较了,它只会一直加这个比较小的数 25 numB += B; //numB = (numB + B) % MOD; 26 } 27 cnt++; 28 } 29 ans = (base + number) % MOD; 30 } 31 return (int)ans; 32 } 33 long long getLcm(int A, int B) { 34 if (A < B) { 35 swap(A, B); 36 } 37 int gcd = getGcd(A, B); 38 long long mul = (long long)A * B; 39 return mul / gcd; 40 } 41 int getGcd(int A, int B) { // A max, B min 42 if (B == 0) {return A;} 43 while (B) { 44 int r = A % B; 45 A = B; 46 B = r; 47 } 48 return A; 49 } 50 };
discuss 里面居然还有人用二分,有空学习一下。
【879】Profitable Schemes (第四题 7分)
Contest 96(2018年11月1日,周四,题号 880-883)
比赛情况记录: 前三题都做出来了,前两题比较稳,第三题不怎么会写,最后直接尾递归搞过了。第四题啊,图论啊,不会做哇。吐血。ranking:199/3916
【883】Projection Area of 3D Shapes (第一题 4分)
题意就是把小方块累起来的一个建筑物求三面的投影数,返回三面投影的总数。
题解:就是单纯的模拟。没啥说的。

1 class Solution { 2 public: 3 int projectionArea(vector<vector<int>>& grid) { 4 const int n = grid.size(), m = grid[0].size(); 5 //cal xy; 6 int xy = 0; 7 for (int i = 0; i < n; ++i) { 8 for (int j = 0; j < m; ++j) { 9 if (grid[i][j]) { 10 xy++; 11 } 12 } 13 } 14 //cal xz (max of col) 15 int xz = 0; 16 for (int j = 0; j < m; ++j) { 17 int maxx = grid[0][j]; 18 for (int i = 1; i < n; ++i) { 19 maxx = max(maxx, grid[i][j]); 20 } 21 xz += maxx; 22 } 23 // cal yz (max of row) { 24 int yz = 0; 25 for (int i = 0; i < n; ++i) { 26 int maxx = grid[i][0]; 27 for (int j = 1; j < m; ++j) { 28 maxx = max(maxx, grid[i][j]); 29 } 30 yz += maxx; 31 } 32 int ans = xy + xz + yz; 33 return ans; 34 } 35 };
【881】Boats to Save People (第二题 5分)
题意:给了一个people数组,和一个 limit 整数。people[i] 代表第 i 个人的体重,每个小船最多可以载两个人,总重量不能超过 limit。问最少要几艘小船才能装完所有人。(题目保证肯定有解,没有人的体重超过 limit)
题解:贪心。先把 people 数组排序,然后最小体重的人和最大体重的人一组,看能不能一艘船,不能的话,就把大体重的人换成一个稍微小体重的人。剩下的没有上船的大体重的人都是自己一艘船。时间复杂度是O(nlogn)。

1 class Solution { 2 public: 3 int numRescueBoats(vector<int>& people, int limit) { 4 const int n = people.size(); 5 sort(people.begin(), people.end()); 6 vector<int> boats(n, -1); //boats[i] means the ith people int boats[i] 7 int p1 = 0, p2 = n-1; 8 int tot = 0; 9 while (p1 < p2) { 10 if (people[p1] + people[p2] <= limit) { 11 boats[p1] = boats[p2] = tot; 12 tot++; 13 p1++, p2--; 14 } else if (people[p1] + people[p2] > limit) { 15 --p2; 16 } 17 } 18 int left = 0; 19 for (auto& p : boats) { 20 if (p == -1) { 21 left++; 22 } 23 } 24 return left + tot; 25 } 26 };
【880】Decoded String at Index (第三题 6分)
题意:给了一个编码过的字符串,如果字符串中的字符是字母的话,那这个字母就是tape中的,如果这个字符是数字 d 的话,就要把前面所有的字符串重复 d 次(共 d 次)。返回整个字符串中下标是 K 的字符。
2 <= S.length <= 100
S
will only contain lowercase letters and digits2
through9
.S
starts with a letter.1 <= K <= 10^9
- The decoded string is guaranteed to have less than
2^63
letters。
题解:比赛的时候我这题是做出来的,但是代码写的太挫了,直接尾递归了。 K 那么大肯定不能生成那么大的字符串,于是我想的是找出每个循环节,然后每次可以缩小 K。

1 class Solution { 2 public: 3 string decodeAtIndex(string S, int K) { 4 vector<long long> repeat; 5 stringstream ss; 6 ss << S; 7 long long len = 0; 8 string use = ""; 9 char c; 10 while (ss) { 11 ss >> c; 12 use += c; 13 if (isdigit(c)) { 14 len = len * (c - '0'); 15 repeat.push_back(len); 16 if (len >= K) {break;} 17 } else { 18 ++len; 19 if (len == K) { 20 string temp = ""; 21 temp += c; 22 return temp; 23 } 24 } 25 } 26 const int n = repeat.size(); 27 long long preLen = repeat.back() / (c - '0'); 28 int left = K % preLen; 29 if (left == 0) { 30 string ans = ""; 31 for (int k = use.size(); k >= 0; --k) { 32 if (use[k] >= 'a' && use[k] <= 'z') { 33 ans += use[k]; 34 break; 35 } 36 } 37 return ans; 38 } 39 return decodeAtIndex(S, left); 40 } 41 };
看了solution:
【882】Reachable Nodes In Subdivided Graph (第四题 7分)
Contest 97(2018年11月3日,周六,题号 884-887)
链接:
比赛情况记录:
Contest 98(2018年11月5日,周一,题号 888-891)
链接:https://leetcode.com/contest/weekly-contest-98
比赛情况记录:做出来了三道题,结果:3/4,ranking:295/2883。看到第四题的时候还有45分钟,有思路,小数据能过,大数据过不了。(为啥大数据过不了我很懵逼)
【888】Fair Candy Swap(第一题 3分)
有两个人小A和小B,每个人都有一个数组作为他们的糖果堆(小A和小B的堆数可能不一样),小A和小B只能交换一堆糖果,返回交换哪堆能让小A和小B的糖果数相等。题目保证有答案。
题解:先求总数然后求一半,这就是最后他们每个人的糖果数。然后枚举小A的一堆,看能不能找到小B中对应的那堆,使得两个人糖果数量相等。

1 class Solution { 2 public: 3 vector<int> fairCandySwap(vector<int>& A, vector<int>& B) { 4 long long summA = 0, summB = 0; 5 int sizeA = A.size(), sizeB = B.size(); 6 for (int i = 0; i < sizeA; ++i) { 7 summA += (long long)A[i]; 8 } 9 for (int i = 0; i < sizeB; ++i) { 10 summB += (long long)B[i]; 11 } 12 long long tot = summA + summB, half = tot / 2; 13 sort(A.begin(), A.end()), sort(B.begin(), B.end()); 14 vector<int> ans; 15 int diff = half - summA; 16 for (int i = 0; i < sizeA; ++i) { 17 int target = A[i] + diff; 18 auto iter = lower_bound(B.begin(), B.end(), target); 19 if (iter != B.end() && *iter == target) { 20 ans.push_back(A[i]); 21 ans.push_back(target); 22 break; 23 } 24 } 25 return ans; 26 } 27 };
【890】Find and Replace Pattern (第二题 5分)
给了一堆words和一个pattern,找出符合pattern形式的所有words返回。
Input: words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb" Output: ["mee","aqq"] Explanation: "mee" matches the pattern because there is a permutation {a -> m, b -> e, ...}. "ccc" does not match the pattern because {a -> c, b -> c, ...} is not a permutation, since a and b map to the same letter.
题解:用个 map 记录对应关系,没有对应关系的话加上,但是有一点需要注意,例子中的数据,abc是不能对应abb(pattern)的,所以还需要一个set记录pattern中的字符是否被别的字符对应了。

1 class Solution { 2 public: 3 vector<string> findAndReplacePattern(vector<string>& words, string pattern) { 4 const int n = words.size(), size = pattern.size(); 5 vector<string> ans; 6 if (n == 0) {return ans;} 7 for (auto& word : words) { 8 bool inAns = true; 9 map<char, char> mp; // word -> pattern 10 set<char> usedPattern; 11 for (int i = 0; i < size; ++i) { 12 if (mp.find(word[i]) == mp.end()) { 13 if (usedPattern.find(pattern[i]) == usedPattern.end()) { 14 mp[word[i]] = pattern[i]; 15 usedPattern.insert(pattern[i]); 16 } else { //conflict 17 inAns = false; 18 break; 19 } 20 } else { 21 if (mp[word[i]] != pattern[i]) { 22 inAns = false; 23 break; 24 } 25 } 26 } 27 if (inAns) { 28 ans.push_back(word); 29 } 30 } 31 return ans; 32 } 33 };
【889】Construct Binary Tree from Preorder and Postorder Traversal (第三题 5分)
给了二叉树的先序遍历和后序遍历,重建二叉树。
题解:我还稍微想了一会儿,主要是靠什么区分左子树的pre,post和右子树的pre,post。通过观察发现,pre的第二个元素一定是左子树的根,post中左子树根之前的元素都属于左子树。所以就能区分左右子树的pre和post数组了。然后递归重建就行了。

1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode* constructFromPrePost(vector<int>& pre, vector<int>& post) { 13 const int n = pre.size(); 14 if (n == 0) {return nullptr;} 15 if (n == 1) { 16 TreeNode* root = new TreeNode(pre[0]); 17 return root; 18 } 19 int rootVal = pre[0], leftRootVal = pre[1]; 20 int idxPostLeftRootVal = 0; 21 for (idxPostLeftRootVal = 0; idxPostLeftRootVal < post.size(); ++idxPostLeftRootVal) { 22 if (leftRootVal == post[idxPostLeftRootVal]) {break;} 23 } 24 int leftPreSize = idxPostLeftRootVal + 1; 25 TreeNode* root = new TreeNode(rootVal); 26 vector<int> leftPre(pre.begin() + 1, pre.begin() + 1 + leftPreSize); 27 vector<int> leftPost(post.begin(), post.begin() + leftPreSize); 28 vector<int> rightPre(pre.begin() + 1 + leftPreSize, pre.end()); 29 vector<int> rightPost(post.begin() + leftPreSize, post.end() - 1); 30 root->left = constructFromPrePost(leftPre, leftPost); 31 root->right = constructFromPrePost(rightPre, rightPost); 32 return root; 33 } 34 };
【891】Sum of Subsequence Widths (第四题 7分)
Contest 99(2018年11月6日,周二凌晨,题号 892-895)
链接:https://leetcode.com/contest/weekly-contest-99
比赛情况记录:第一题有点卡住,后来还是解决了。30分钟做了三道题,第四题放弃了。总之我这周要狂攻hard题。结果: 3/4, ranking:286/3153
【892】Surface Area of 3D Shapes(第一题 4分)
给了一个 N * N 的grid,我们想放 1 * 1 * 1 的 cubes,v = grid[i][j] 相当于在坐标(i, j)处放了一个高度 v 的塔。返回所搭建模型的表面积。
Example 1: Input: [[2]] Output: 10
Example 2: Input: [[1,2],[3,4]] Output: 34
Example 3: Input: [[1,0],[0,2]] Output: 16
Example 4: Input: [[1,1,1],[1,0,1],[1,1,1]] Output: 32
Example 5: Input: [[2,2,2],[2,1,2],[2,2,2]] Output: 46
题解:我本来想用每行每列的最大值直接当作侧面然后乘以2的,结果我发现那个模型可能里面有个凹坑。比如 example 4.和 example 5. 后来就一行一行,一列一列的求差,求侧面积。

1 class Solution { 2 public: 3 int surfaceArea(vector<vector<int>>& grid) { 4 const int n = grid.size(); 5 int front = 0, side = 0, top = 0; 6 for (int i = 0; i < n; ++i) { 7 side += grid[i][0]; 8 for (int j = 0; j < n; ++j) { 9 if (grid[i][j]) { top++; } 10 if (j > 0) { 11 side += abs(grid[i][j] - grid[i][j-1]); 12 } 13 } 14 side += grid[i][n-1]; 15 } 16 for (int j = 0; j < n; ++j) { 17 front += grid[0][j]; 18 for (int i = 1; i < n; ++i) { 19 front += abs(grid[i][j] - grid[i-1][j]); 20 } 21 front += grid[n-1][j]; 22 } 23 //printf("side = %d, front = %d ", side, front); 24 int ret = top * 2 + side + front; 25 return ret; 26 } 27 };
【893】Groups of Special-Equivalent Strings (第二题 4分)
字符串 S 等价于 字符串T的定义是 S字符串的奇数和奇数下标元素间任意交换,偶数和偶数下标之间任意交换,最后可以变换成 T。给了一个字符串数组,题目保证数组里面的字符串长度都是一样的。问这个数组能分成几个小组,每个小组里面的字符串都是等价的。
题解:我是设计了一种数据结构 set<pair<string, string>> 用这个 pair 结构来存每个单词的奇数下标组成的字符串,偶数下标组成的字符串(需要排序),结果就是 set 的大小。

1 class Solution { 2 public: 3 int numSpecialEquivGroups(vector<string>& A) { 4 int ret = 0; 5 const int n = A.size(), m = A[0].size(); 6 set<pair<string, string>> st; //odd, even 7 for (auto& w : A) { 8 string odd = "", even = ""; 9 for (int i = 0; i < m; ++i) { 10 if (i & 1) { 11 odd += string(1, w[i]); 12 } else { 13 even += string(1, w[i]); 14 } 15 } 16 sort(odd.begin(), odd.end()); 17 sort(even.begin(), even.end()); 18 st.insert(make_pair(odd, even)); 19 } 20 ret = st.size(); 21 return ret; 22 } 23 };
【894】All Possible Full Binary Trees (第三题 6分)
一个满二叉树的定义是一个结点要么左右两个儿子,要么它是叶子结点。给了一个数字 N, 要求返回 N 个结点的满二叉树的所有形态。
题解:这棵满二叉树的左儿子的大小可能是 {1, 3, 5 .. n - 2}, 右儿子的大小可能是 {n - 2, n - 4, .. ,1}。我们可以递归的生成左儿子和右儿子的集合,然后左儿子里选一个,右儿子里面选一个,再加上根节点组成一棵二叉树。

1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<TreeNode*> allPossibleFBT(int N) { 13 vector<TreeNode*> ret; 14 if (N == 0) {return ret;} 15 if (N == 1) { 16 TreeNode* root = new TreeNode(0); 17 ret.push_back(root); 18 return ret; 19 } 20 21 for (int leftSize = 1; leftSize < N - 1; leftSize = leftSize + 2) { 22 int rightSize = N - 1 - leftSize; 23 vector<TreeNode*> left = allPossibleFBT(leftSize); 24 vector<TreeNode*> right = allPossibleFBT(rightSize); 25 for (auto l : left) { 26 for (auto r : right) { 27 TreeNode* root = new TreeNode(0); 28 root->left = l; 29 root->right = r; 30 ret.push_back(root); 31 } 32 } 33 } 34 return ret; 35 } 36 37 };
【895】Maximum Frequency Stack (第四题 8分)