【Leetcode周赛】从contest-111开始。(一般是10个contest写一篇文章)
Contest 111 (题号941~944)(2019年1月19日,补充题解,主要是943题)
链接:https://leetcode.com/contest/weekly-contest-111
【941】Valid Mountain Array(第一题 3分)(google tag)
判断一个数组是不是 Mountain 数组。 Mountain 数组的定义是
A.length >= 3
There exists some i with 0 < i < A.length - 1 such that:
A[0] < A[1] < ... A[i-1] < A[i]
A[i] > A[i+1] > ... > A[B.length - 1]
题解: 先判断元素个数是不是比 3 大,然后想象两拨人爬山,这个定义就是看最后两拨人是不是爬到了同一个山顶。
1 class Solution { 2 public: 3 bool validMountainArray(vector<int>& A) { 4 const int n = A.size(); 5 int start = 0, end = n - 1; 6 while (start + 1 < n && A[start] < A[start+1]) { 7 ++start; 8 } 9 while (end - 1 >= 0 && A[end-1] > A[end]) { 10 --end; 11 } 12 return start > 0 && start == end && end < n-1; 13 } 14 };
Delete Columns to Make Sorted(第二题 4分)
DI String Match(第三题 5分)
【943】Find the Shortest Superstring(第四题 8分)
Given an array A of strings, find any smallest string that contains each string in A
as a substring.
We may assume that no string in A
is substring of another string in A
.
Example 1: Input: ["alex","loves","leetcode"] Output: "alexlovesleetcode" Explanation: All permutations of "alex","loves","leetcode" would also be accepted. Example 2: Input: ["catg","ctaagt","gcta","ttca","atgcatc"] Output: "gctaagttcatgcatc" Note: 1 <= A.length <= 12 1 <= A[i].length <= 20
题解:本题可以看成旅行商问题,用状态压缩的动态规划来解。我们先给每个单词都标号,然后从word1到word2重合之后,word2长出来的长度就是图的权重。比如 word1 = abcd, word2 = bcdfsd, g[word1][word2] = 3.
然后我们想走过每一个单词,使得图的总权重最小。这个就是旅行商问题的定义。我们用一个二进制 11110111, 表示除了第三个单词不在,其他七个单词都存在。这个就是状态压缩。(用一个二进制表示一个set中的元素是否存在)
《挑战设计程序竞赛》3.4.1
假设我们现在已经访问过顶点的集合是S,当前所在的顶点为 v, 用 dp[S][v]表示从v出发访问剩余的所有顶点,最终回到顶点0的路径的权重总和的最小值。由于从 v 出发可以移动到任意的一个节点 u 不属于 S,因此有下面的递推式。
dp[V][0] = 0;
dp[S][v] = min{dp[S U {u}][u] + d(v, u) | u 不属于 S }
1 class Solution { 2 public: 3 const int MAX = 12; 4 string shortestSuperstring(vector<string>& A) { 5 const int n = A.size(); 6 vector<vector<int>> g = initGraph(A); 7 vector<vector<int>> dp(1 << MAX, vector<int>(n, INT_MAX)); 8 int path[1 << MAX][MAX] = {INT_MAX}; 9 int minLen = INT_MAX, last = -1; 10 for (int S = 0; S < 1 << n; ++S) { 11 for (int j = 0; j < n; ++j) { 12 if ((S & (1 << j)) > 0) { //一定要加括号,不然先算 (1 << j) > 0 再算 位运算 13 int prev = S - (1 << j); 14 if (prev == 0) { 15 dp[S][j] = A[j].size(); 16 } else { 17 for (int k = 0; k < n; ++k) { 18 if (dp[prev][k] < INT_MAX && dp[prev][k] + g[k][j] < dp[S][j]) { 19 dp[S][j] = min(dp[prev][k] + g[k][j], dp[S][j]); 20 path[S][j] = k; 21 } 22 } 23 } 24 } 25 if (S == ((1 << n) - 1) && dp[S][j] < minLen) { 26 minLen = dp[S][j]; 27 last = j; 28 } 29 } 30 } 31 // build string. 32 string ret = ""; 33 int index = last, pre = last, state = (1 << n) -1; 34 vector<int> seq(n); 35 for (int i = n-1; i >= 0; --i) { 36 seq[i] = index; 37 pre = path[state][index]; 38 state = state - (1 << index); 39 // printf ("index = %d, state = %d, pre = %d \n", index, state, pre); 40 index = pre; 41 } 42 43 ret = A[seq[0]]; 44 for (int i = 1; i < n; ++i) { 45 string str = A[seq[i]]; 46 ret += str.substr(str.size() - g[seq[i-1]][seq[i]]); 47 } 48 return ret; 49 } 50 vector<vector<int>> initGraph(vector<string>& A) { 51 const int n = A.size(); 52 vector<vector<int>> g(n, vector<int>(n, 0)); 53 for (int i = 0; i < A.size(); ++i) { 54 for (int j = i; j < A.size(); ++j) { 55 g[i][j] = cal(A[i], A[j]); 56 g[j][i] = cal(A[j], A[i]); 57 } 58 } 59 return g; 60 } 61 int cal(string s1, string s2) { //A[u] = s1, A[v] = s2 62 const int n1 = s1.size(), n2 = s2.size(); 63 int ret = n2; 64 for (int k = 1; k < n1; ++k) { 65 string sub1 = s1.substr(k); // len = n1 - k 66 string sub2 = s2.substr(0, n1-k); // len = n1 - k; 67 if (sub1 == sub2) { 68 ret = min(ret, n2 - n1 + k); 69 break; 70 } 71 } 72 return ret; 73 } 74 void print(vector<vector<int>>& g) { 75 const int n = g.size(); 76 const int m = g[0].size(); 77 for (int i = 0; i < n; ++i) { 78 for (int j = 0; j < m; ++j) { 79 printf("%d ", g[i][j]); 80 } 81 printf("\n"); 82 } 83 } 84 };
Contest 112(2018年11月25日)(题号945~948)
比赛情况记录:这场有点像是贪心专题了orz,结果:1/4,ranking:1383/3195,第一题懵逼的原因是 5-3=3?==。这场真的没有一题难题,然后就是不会做。这就比较尴尬了。
链接:https://leetcode.com/contest/weekly-contest-112
【945】Minimum Increment to Make Array Unique(第一题 5分)
题意是给了一个数组有重复数字,目标是把数组中的元素都变成 unique 的,游戏规则是移动一步可以把一个数字加一。问最少移动几步能把所有数字unique化。
题解:先排序,然后按照排序的数字递增,缺几个就加几。
1 class Solution { 2 public: 3 int minIncrementForUnique(vector<int>& A) { 4 sort(A.begin(), A.end()); 5 const int n = A.size(); 6 int res = 0; 7 for (int i = 1; i < n; ++i) { 8 if (A[i] > A[i-1]) {continue;} 9 int newAi = A[i-1]+1; 10 res += newAi - A[i]; 11 A[i] = newAi; 12 } 13 return res; 14 } 15 };
【946】Validate Stack Sequences(第二题 6分)(我没记错的话这题应该是剑指offer原题)
给了两个 distinct 的序列,问一个作为栈的 push 序列的话,另外一个是否能作为 pop 序列。
题解:直接模拟栈的行为。
1 class Solution { 2 public: 3 bool validateStackSequences(vector<int>& pushed, vector<int>& popped) { 4 const int n = pushed.size(); 5 stack<int> stk; 6 int p1 = 0, p2 = 0; 7 while (p1 < n && p2 < n) { 8 while (stk.empty() || stk.top() != popped[p2]) { 9 stk.push(pushed[p1++]); 10 } 11 while (!stk.empty() && stk.top() == popped[p2]) { 12 stk.pop(); 13 ++p2; 14 } 15 } 16 while (!stk.empty() && stk.top() == popped[p2]) { 17 stk.pop(); 18 ++p2; 19 } 20 return stk.empty(); 21 } 22 };
【947】Most Stones Removed with Same Row or Column(第三题 6分)(2019年1月30日,谷歌tag复习)
【948】Bag of Tokens(第四题 6分)(2018年12月9日,算法群每日一题,补充的周赛解题报告)
题意是给了一个tokens的数组,和一个原始的power P,游戏规则如下:(1)用 tokens[i] 的 power 可以换 1 point; (2) 用 1 point 也可以换 tokens[i] 的power。问最多能换多少 point。
题解:greedy + 2 pointers。排序之后用2 pointers 两遍夹逼。前面的pointer用来换point(一直往前直到当前的power换不起point了), 后面的pointer用来换power。
1 class Solution { 2 public: 3 int bagOfTokensScore(vector<int>& tokens, int P) { 4 const int n = tokens.size(); 5 sort(tokens.begin(), tokens.end()); //1. sort 6 int points = 0, ret = 0; 7 //2 .2 pointers, boundary 8 int p1 = 0, p2 = n - 1; 9 while (p1 <= p2) { 10 int preP1 = p1, preP2 = p2; 11 while (p1 <= p2 && P >= tokens[p1]) { 12 points += 1; 13 P -= tokens[p1++]; 14 } 15 ret = max(points, ret); 16 if (p1 <= p2 && points >= 1) { 17 points -= 1; 18 P += tokens[p2--]; 19 } 20 if (p1 == preP1 && p2 == preP2) { 21 break; 22 } 23 } 24 return ret; 25 } 26 };
Contest 113 (2018年12月2日)(题号949~952)
比赛情况记录:结果:3/4, ranking: 708/3549。起床晚了,第一题手残调试点成提交,懵逼的WA了三次。第二题跟 symmetric tree 那个很像。第三题跟前几周周赛的第四题很像。第四题他们说是线性筛法求素数。
链接:https://leetcode.com/contest/weekly-contest-113
【949】Largest Time for Given Digits(第一题 4分)
给了一个数组里面四个整数(0-9),返回这个数组能生成的最大时间表示 HH:MM。
题解:暴力生成所有的排列,然后判断是否符合时间的定义,然后用ret变量标记最大的字符串。(时间相关的类似题:lc 681)
1 class Solution { 2 public: 3 string largestTimeFromDigits(vector<int>& A) { 4 sort(A.begin(), A.end()); 5 if (A[0] >= 3) {return "";} 6 string ret = ""; 7 do { 8 string str = string(1, A[0] + '0') + string(1, A[1] + '0') + ":" + string(1, A[2] + '0') + string(1, A[3] + '0'); 9 if (isValid(str)) { 10 if (str > ret) { 11 ret = str; 12 } 13 } 14 } while(next_permutation(A.begin(), A.end())); 15 return ret; 16 } 17 bool isValid(string str) { 18 if (str[0] > '2') { return false;} 19 if (str[0] == '2' && str[1] >= '4') { return false; } 20 if (str[3] >= '6') { return false;} 21 return true; 22 } 23 };
【951】Flip Equivalent Binary Trees(第二题 5分)
For a binary tree T, we can define a flip operation as follows: choose any node, and swap the left and right child subtrees.
A binary tree X is flip equivalent to a binary tree Y if and only if we can make X equal to Y after some number of flip operations.
Write a function that determines whether two binary trees are flip equivalent. The trees are given by root nodes root1
and root2
.
题解:我先判断有没有根,有根但是值不相等的话,就返回false,递归判断 root1 的左儿子和 root2 的左儿子 以及 root1 的右儿子和 root2 的右儿子 是不是 FlipEqui,如果是直接返回yes。否则递归判断 root1 的右儿子和 root2 的左儿子 以及 root1 的左儿子和 root2 的右儿子 是不是 FlipEqui,是的话,返回yes。都不行就返回false。(类似题:lc 101)
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 flipEquiv(TreeNode* root1, TreeNode* root2) { 13 if (!root1 && !root2) {return true;} 14 if (!root1 || !root2) {return false;} 15 if (root1->val != root2->val) {return false;} 16 bool res1 = flipEquiv(root1->left, root2->left) && flipEquiv(root1->right, root2->right); 17 if (res1) { 18 return res1; 19 } 20 bool res2 = flipEquiv(root1->left, root2->right) && flipEquiv(root1->right, root2->left); 21 if (res2) { 22 return res2; 23 } 24 return false; 25 } 26 };
【950】Reveal Cards In Increasing Order(第三题 5分)
我们有一个 N 张的 unique 纸牌,我们希望得到一个序列,按照下面步骤操作之后,从这个序列弹出的序列是完全排好序的。
(1)弹出最上面一张纸牌 A1。
(2)如果 A1 下面有纸牌 A2,把 A2 放到这叠纸牌的最下方。
依次循环做(1)(2)操作。
Example 1: Input: [17,13,11,2,3,5,7] Output: [2,13,3,11,5,17,7] Explanation: We get the deck in the order [17,13,11,2,3,5,7] (this order doesn't matter), and reorder it. After reordering, the deck starts as [2,13,3,11,5,17,7], where 2 is the top of the deck. We reveal 2, and move 13 to the bottom. The deck is now [3,11,5,17,7,13]. We reveal 3, and move 11 to the bottom. The deck is now [5,17,7,13,11]. We reveal 5, and move 17 to the bottom. The deck is now [7,13,11,17]. We reveal 7, and move 13 to the bottom. The deck is now [11,17,13]. We reveal 11, and move 17 to the bottom. The deck is now [13,17]. We reveal 13, and move 17 to the bottom. The deck is now [17]. We reveal 17. Since all the cards revealed are in increasing order, the answer is correct.
题解:先把数组排序好了之后,从小到大依次隔空插入。
1 class Solution { 2 public: 3 vector<int> deckRevealedIncreasing(vector<int>& deck) { 4 const int n = deck.size(); 5 sort(deck.begin(), deck.end()); 6 vector<int> ret(n, -1); 7 int gap = 1, k = 0; 8 while (k < n) { 9 for (int i = 0; i < n; ++i) { 10 if (ret[i] != -1) {continue;} 11 if (gap) { 12 ret[i] = deck[k]; 13 ++k; 14 gap = 0; 15 if (k >= n) {break;} 16 } else { 17 if (ret[i] == -1) {gap = 1;} 18 } 19 } 20 } 21 return ret; 22 } 23 };
【952】Largest Component Size by Common Factor (第四题 8分)
Contest 114(2018年12月9日)(题号953-956)
比赛情况记录:本周一直比较萎靡,唉,结果:2/4,ranking: 696 / 3198,我认为第三题第四题都不难,然而实现上有困难,肯定是我的问题了。
链接:https://leetcode.com/contest/weekly-contest-114
【953】Verifying an Alien Dictionary(第一题 4分)
有一种外星文字也包含 'a’ 到 'z' 26个字母,只不过字母顺序不一样罢了。给了一堆 words,判断这些 words 是不是按照外星字典顺序排列的。
题解:我用 unordered_map 存储了外星字母对应英文字母的关系,然后把每一个 word 转换成英文单词,看这些单词是否符合字典顺序。
1 class Solution { 2 public: 3 bool isAlienSorted(vector<string>& words, string order) { 4 //1. map relationship 5 unordered_map<char, char> mp; 6 for (int i = 0; i < 26; ++i) { 7 mp[order[i]] = i + 'a'; 8 } 9 //2. change word 10 vector<string> inputs = words; 11 for (auto& w : inputs) { 12 for (auto& c : w) { 13 c = mp[c]; 14 } 15 } 16 //3. check order 17 for (int i = 1; i < inputs.size(); ++i) { 18 if (inputs[i] < inputs[i-1]) { 19 return false; 20 } 21 } 22 return true; 23 } 24 };
【954】Array of Doubled Pairs(第二题 5分)
【955】Delete Columns to Make Sorted II(第三题 6分)(google tag)
给了一个字符串的数组,要求删除其中的几列,使得字符串数组字典序单调递增。返回需要删除的最小的列数。
Example 1: Input: ["ca","bb","ac"] Output: 1 Explanation: After deleting the first column, A = ["a", "b", "c"]. Now A is in lexicographic order (ie. A[0] <= A[1] <= A[2]). We require at least 1 deletion since initially A was not in lexicographic order, so the answer is 1. Example 2: Input: ["xc","yb","za"] Output: 0 Explanation: A is already in lexicographic order, so we don't need to delete anything. Note that the rows of A are not necessarily in lexicographic order: ie. it is NOT necessarily true that (A[0][0] <= A[0][1] <= ...) Example 3: Input: ["zyx","wvu","tsr"] Output: 3 Explanation: We have to delete every column. Note: 1 <= A.length <= 100 1 <= A[i].length <= 100
题解:我们可以有个很直观的想法,如果当前的这列加上去之后,会导致A[i] > A[i+1] 那么这列就可以删除了。我们可以设置一个空的字符串数组mat,代表A delete col之后的结果。依次遍历每一列,看要不要删除。如果删除,就res++,否则mat上加上这一列。
更进一步,我们可以用 vector<bool> sorted 来优化 mat 数组,sorted[i] = true 代表 A[i] < A[i+1] 已经严格小于了。这样我们检查下一列的时候就不需要考虑这两个字符串的大小关系可以直接跳过。
1 class Solution { 2 public: 3 int minDeletionSize(vector<string>& A) { 4 const int n = A.size(), m = A[0].size(); 5 vector<bool> sorted(n, false); 6 int res(0); 7 for (int k = 0; k < m; ++k) { 8 bool needDelete = false; 9 for (int i = 0; i < n-1; ++i) { 10 if (!sorted[i] && A[i][k] > A[i+1][k]) { 11 res++; needDelete = true; 12 break; 13 } 14 } 15 if (needDelete) {continue;} 16 for (int i = 0; i < n - 1; ++i) { 17 if (!sorted[i] && A[i][k] < A[i+1][k]) { 18 sorted[i] = true; 19 } 20 } 21 } 22 return res; 23 } 24 };
【956】Tallest Billboard(第四题 8分)
Contest 115(2018年12月16日)(题号957-960)
比赛情况记录:本周比赛马马虎虎吧,第四题不会做比较尴尬。结果: 2/4,ranking: 490/3055。第三题看了题真是没啥想法。第四题是个LIS的变种题(把一列看成一个元素,自己实现比较大小)。
链接:https://leetcode.com/contest/weekly-contest-115
【958】Check Completeness of a Binary Tree(第一题 5分)
检查一棵树是不是完全二叉树。
题解:第一题有稍微有点卡住了。有两种检测方法,都不是那么的直观。第一种是 dfs,判断是不是左右子树都是完全二叉树,然后判断左子树高度最多比右子树高度大一。第二种是 bfs,判断方法是
(1)如果当前结点有右孩子但是没有左孩子,那么直接返回false。
(2)如果当前结点不是左右孩子都有,那么后面的结点都必须是叶子结点。
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 isCompleteTree(TreeNode* root) { 13 if (!root) {return true;} 14 queue<TreeNode*> que; 15 que.push(root); 16 bool mark = false; 17 while (!que.empty()) { 18 int size = que.size(); 19 for (int i = 0; i < size; ++i) { 20 TreeNode* cur = que.front(); que.pop(); 21 if (mark && (cur->left || cur->right)) { 22 return false; 23 } 24 if (cur->left) { 25 que.push(cur->left); 26 } 27 if (cur->right) { 28 que.push(cur->right); 29 } 30 if (cur->left && !cur->right || !cur->left && !cur->right) { 31 mark = true; 32 } 33 if (cur->right && !cur->left) { 34 return false; 35 } 36 } 37 } 38 return true; 39 } 40 };
【957】Prison Cells After N Days(第二题 6分)
有一个数组里面有 8 个0/1元素, 分别代表8个囚室,0代表囚室为空,1代表囚室有人。每天囚室的人员分布都在变化。变化规则就是如下两条:
- If a cell has two adjacent neighbors that are both occupied or both vacant, then the cell becomes occupied.
- Otherwise, it becomes vacant.
给了 Day 0 的囚室分布,问第 N 天的囚室分布。(1 <= N <= 10^9
)
题解:这题肯定是有循环节的。我们可以注意到第一个 cell 和最后一个 cell 肯定从第一天开始就都是 0 了,因为他们就没有两个邻居。然后中间的 6个 cell,它们要么是 0 要么是 1。所以循环节最多也就是 2^6 = 64 个排列。我们用个数组记录每个vector代表的二进制数。然后我们找循环节就行了。
1 class Solution { 2 public: 3 vector<int> prisonAfterNDays(vector<int>& cells, int N) { 4 int number = vector2Int(cells); 5 vector<int> array(256, -1); 6 vector<int> nCells = cells; 7 int day = 0; 8 do { 9 array[day++] = number; 10 number = transNextDay(nCells); 11 if (day == N + 1) { 12 break; 13 } 14 } while (find(array.begin(), array.end(), number) == array.end()); 15 if (day == N + 1) { 16 vector<int> ret = getVector(array[N]); 17 return ret; 18 } 19 auto iter = find(array.begin(), array.end(), number); 20 const int startDay = distance(array.begin(), iter); 21 const int cycle = day - startDay; 22 int idx = ((N - startDay) % cycle); 23 vector<int> ret = getVector(array[startDay + idx]); 24 return ret; 25 } 26 int vector2Int(const vector<int>& nums) { 27 int pow = 1, ret = 0; 28 for (int i = 0; i < 8; ++i) { 29 ret += pow * nums[i]; 30 pow *= 2; 31 } 32 return ret; 33 } 34 int transNextDay(vector<int>& nCells) { 35 vector<int> newCells(8, 0); 36 for (int i = 1; i < 8 -1; ++i) { 37 if (nCells[i-1] ^ nCells[i+1] == 0) { 38 newCells[i] = 1; 39 } 40 } 41 int ret = vector2Int(newCells); 42 nCells = newCells; 43 return ret; 44 } 45 vector<int> getVector(int num) { 46 vector<int> ret(8, 0); 47 for (int i = 0; i < 8; ++i) { 48 ret[i] = num % 2; 49 num /= 2; 50 } 51 return ret; 52 } 53 };
【959】Regions Cut By Slashes(第三题 7分)
【960】Delete Columns to Make Sorted III(第四题 7分)
给了一个字符串数组 A,A[i] 表示一个字符串,我们要从字符矩阵A中删除某几列字符,使得每个字符串都是符合字母序的 (every element (row) in lexicographicorder)。问最少删除几列。
题解:这个题目有点像是 LIS,但是比赛的时候没想出来,我想的是每行都 LIS,然后求出 min(LIS) = x,在[1, x]中二分求最大的k。但是其实不是这样想的,这样暴力解复杂度太高,又难写。本题事实上应该把每一列都看成 LIS 的一个元素,然后我们自己定义元素的大小比较关系就行了。时间复杂度是 O(m*m*n)
1 class Solution { 2 public: 3 int minDeletionSize(vector<string>& A) { 4 const int n = A.size(), m = A[0].size(); 5 vector<int> dp(m, 1); 6 int maxLen = 1; 7 for (int i = m - 2; i >= 0; --i) { 8 for (int j = i + 1; j < m; ++j) { 9 bool found = false; 10 for (int k = 0; k < n; ++k) { 11 if (A[k][i] > A[k][j]) { 12 found = true; 13 break; 14 } 15 } 16 if (!found) { 17 dp[i] = max(dp[i], dp[j] + 1); 18 maxLen = max(maxLen, dp[i]); 19 } 20 } 21 } 22 return m - maxLen; 23 } 24 };
Contest 116(2018年12月23日)(题号961-964)
链接:https://leetcode.com/contest/weekly-contest-116
比赛情况记录:结果: 1/4,ranking: 1745/2965. 这两天我也不知道怎么了,锻炼练不下去,题目不想做,唉。希望只是一时状态不好而已。
【961】N-Repeated Element in Size 2N Array(第一题 2分)
【962】Maximum Width Ramp(第二题 5分)
给了一个数组 A,想找一对 tuple (i, j),满足 i < j && A[i] <= A[j]. 找到gap间距最大的 i, j, 返回 max(gap) 。
题解:看了lee215的解答,他说用 decreasing stack。其实我到现在都没有彻底get到这个点在哪里。吐血了。
1 class Solution { 2 public: 3 int maxWidthRamp(vector<int>& A) { 4 const int n = A.size(); 5 stack<int> stk; 6 for (int i = 0; i < n; ++i) { 7 if (stk.empty() || A[stk.top()] > A[i]) { 8 stk.push(i); 9 } 10 } 11 int ret = 0; 12 for (int i = n-1; i > 0; --i) { 13 while (!stk.empty() && A[i] >= A[stk.top()]) { 14 printf("i = %d, stk.top() = %d, A[i] = %d, A[stk.top()] = %d\n", i, stk.top(), A[i], A[stk.top()]); 15 ret = max(i - stk.top(), ret); 16 stk.pop(); 17 } 18 } 19 return ret; 20 } 21 };
【963】Minimum Area Rectangle II(第三题 5分)
【964】Least Operators to Express Number (第四题 9分)
Contest 117(2018年12月30日)(题号965-968)
比赛情况记录:今天早上爬起来看群主mock interview,然后回去睡了一个回笼觉,结果就实在起不来了。所以这次是比赛之后马上开的virtual,virtual结果: 3/4,ranking:495/2770.
【965】Univalued Binary Tree(第一题 3分)
给了一棵二叉树判断整棵数是不是所有结点都是一个值。保证至少有一个根结点。
题解:dfs 或者 bfs 随你喜欢。
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 isUnivalTree(TreeNode* root) { 13 if (!root) {return true;} 14 const int value = root->val; 15 queue<TreeNode*> que; 16 que.push(root); 17 bool ret = true; 18 while (!que.empty()) { 19 auto cur = que.front(); que.pop(); 20 if (cur->val != value) { 21 ret = false; 22 break; 23 } 24 if (cur->left) { 25 que.push(cur->left); 26 } 27 if (cur->right) { 28 que.push(cur->right); 29 } 30 } 31 return ret; 32 } 33 34 };
【967】Numbers With Same Consecutive Differences(第二题 5分)
返回所有长度为 N 的非负整数,相邻两个数位的差的绝对值是 K。前缀 0 的数是非法的,所以不要包含前缀 0 的数,比如 01,0 本身是合法的。
题解:dfs。我写的比较唠叨了,这题还可以更简洁。简洁版本有待补充。
1 class Solution { 2 public: 3 vector<int> numsSameConsecDiff(int N, int K) { 4 vector<int> ret; 5 if (K == 0) { 6 string strMul = string(N, '1'); 7 const int mul = stoi(strMul); 8 for (int i = 1; i <= 9; ++i) { 9 int number = i * mul; 10 ret.push_back(number); 11 } 12 if (N == 1) { 13 ret.push_back(0); 14 } 15 return ret; 16 } 17 for (int i = 1; i <= 9; ++i) { 18 string strNum = to_string(i); 19 dfs(strNum, N - 1, K, ret); 20 } 21 if (N == 1) { 22 ret.push_back(0); 23 } 24 return ret; 25 } 26 void dfs(string& strNum, int leftSize, const int K, vector<int>& ret) { 27 if (leftSize == 0) { 28 ret.push_back(stoi(strNum)); 29 return; 30 } 31 int back = strNum.back() - '0'; 32 if (back + K <= 9) { 33 string strLastDigit = to_string(back + K); 34 string strNew = strNum + strLastDigit; 35 dfs(strNew, leftSize - 1, K, ret); 36 } 37 if (back - K >= 0) { 38 string strLastDigit = to_string(back - K); 39 string strNew = strNum + strLastDigit; 40 dfs(strNew, leftSize - 1, K, ret); 41 } 42 return; 43 } 44 45 };
【966】Vowel Spellchecker(第三题 6分)
给了一个单词表和一大堆 query,每次 query 给一个单词,问这个单词能不能通过给出的规则变成单词表里面的词。能的话,返回第一个单词表里面对应的单词。
规则一。query的单词本身就在单词表中。
规则二。query的单词通过更改大小写字母变换成单词表中单词。
规则三。query的单词通过更换元音字母变换成单词表中单词。(这个匹配大小写不敏感)
规则优先级按照一二三递减。
题解:我这题一开始超时了,后来翻了一下群记录,原来要把元音化成通配符,类似pattern那种感觉。
1 class Solution { 2 public: 3 vector<string> spellchecker(vector<string>& wordlist, vector<string>& queries) { 4 set<string> stWord(wordlist.begin(), wordlist.end()); 5 const int n = queries.size(); 6 vector<string> ret(n, ""); 7 set<char> stTemp = {'a', 'e', 'i', 'o', 'u'}; 8 stVowel = stTemp; 9 //build map 10 for (int idx = 0; idx < wordlist.size(); ++idx) { 11 string strNew = str2Lower(wordlist[idx]); 12 if (mp.find(strNew) == mp.end()) { 13 mp[strNew] = idx; 14 } 15 string s2 = str2Pattern(strNew); 16 if (mpVowels.find(s2) == mpVowels.end()) { 17 mpVowels[s2] = idx; 18 } 19 } 20 for (int i = 0; i < n; ++i) { 21 string w = queries[i]; 22 if (stWord.find(w) != stWord.end()) { //rule1 23 ret[i] = w; 24 } else if (mp.find(str2Lower(w)) != mp.end()) { //rule2 25 int idx = mp[str2Lower(w)]; 26 ret[i] = wordlist[idx]; 27 } else { //rule3 28 int idx = checkRule3(w); 29 if (idx == -1) { continue; } 30 ret[i] = wordlist[idx]; 31 } 32 } 33 return ret; 34 } 35 unordered_map<string, int> mp, mpVowels; 36 set<char> stVowel; 37 inline string str2Lower(string str) { 38 string strNew = str; 39 for (int i = 0; i < strNew.size(); ++i) { 40 if (isupper(strNew[i])) { 41 strNew[i] = tolower(strNew[i]); 42 } 43 } 44 return strNew; 45 } 46 inline string str2Pattern(string str) { 47 string strNew = str; 48 for (auto & c: strNew) { 49 if (stVowel.find(c) != stVowel.end()) { 50 c = '*'; 51 } 52 } 53 return strNew; 54 } 55 int checkRule3 (string str) { 56 string strNew = str2Lower(str); 57 strNew = str2Pattern(strNew); 58 int ret = -1; 59 if (mpVowels.find(strNew) != mpVowels.end()) { 60 ret = mpVowels[strNew]; 61 } 62 return ret; 63 } 64 };
【968】Binary Tree Cameras(第四题 8分)
Contest 118(2019年1月6日)(题号969-972)
链接:https://leetcode.com/contest/weekly-contest-118
总结:rank 730/3587, 结果:2/4。 具体比赛的时候的想法已经忘没了,尴尬。噢,第二题我本来是想出来的,结果手残了还没看出来。
【970】Powerful Integers(第一题 3分)
给了两个非负整数,x 和 y, 以及一个数 bound,求表达式 x^i + y^j 中所有小于等于 bound 的值,用数组的形式返回。其中 i >= 0, j >= 0。
题解:直接暴力,注意 x = 1 和 y = 1 的时候怎么办。不要写死循环了。
1 class Solution { 2 public: 3 vector<int> powerfulIntegers(int x, int y, int bound) { 4 int curx = 1, cury = 1; 5 set<int> ret; 6 for (int i = 0; curx + cury <= bound; ++i) { 7 for (int j = 0; curx + cury <= bound; ++j) { 8 if (curx + cury <= bound) { 9 ret.insert(curx + cury); 10 } 11 cury *= y; 12 if (cury * y == cury) { 13 break; 14 } 15 } 16 if (curx * x == curx) { 17 break; 18 } 19 curx *= x; 20 cury = 1; 21 } 22 vector<int> ans; 23 for (auto s : ret) { 24 ans.push_back(s); 25 } 26 return ans; 27 } 28 };
【969】Pancake Sorting(第二题 5分)
煎饼排序,一次煎饼翻转的操作如下:我们可以选一个正整数 k,然后翻转前 k 个煎饼。输入是一个 A 数组,代表煎饼的大小,输出是一个 k 的数组,代表经过这个 k 数组的操作之后,A数组可以变成有序的。(k 的长度不超过 10 * A.size())
题解:我们可以考虑每次操作都使得一个煎饼放在最下方。可以这么操作,选出没有排序的数组中的最大煎饼,然后翻转一次,把最大的煎饼翻转到第一个位置,然后翻转整个没有排序的数组,把最大的煎饼从第一个位置换到最后一个位置。这样产生的 K 数组的大小是 2N。
1 class Solution { 2 public: 3 vector<int> pancakeSort(vector<int>& A) { 4 vector<int> copyA(A), ret; 5 sort(copyA.begin(), copyA.end()); 6 if (A == copyA) { 7 return ret; 8 } 9 n = A.size(); 10 flip(A, ret, n, copyA); 11 // print(A); 12 return ret; 13 } 14 void flip(vector<int>& A, vector<int>& ret, int cur, const vector<int> sortedA) { 15 if (cur == 1) { 16 return; 17 } 18 auto iter = find(A.begin(), A.end(), cur); 19 const int idx = distance(A.begin(), iter); 20 if (idx != 0) { 21 reverse(A.begin(), iter + 1); 22 // print(A); 23 ret.push_back(idx + 1); 24 } 25 if (A == sortedA) { 26 return; 27 } 28 reverse(A.begin(), A.begin() + cur); 29 // print(A); 30 ret.push_back(cur); 31 if (A == sortedA) { 32 return; 33 } 34 flip(A, ret, cur-1, sortedA); 35 } 36 void print(vector<int>& A) { 37 for (auto e : A) { 38 printf("%d ", e); 39 } 40 printf("\n"); 41 } 42 int n; 43 };
【971】Flip Binary Tree To Match Preorder Traversal(第三题 6分)
【972】Equal Rational Numbers(第四题 8分)(第四题比赛的时候不会做,2019年1月17日算法群里的每日一题)
给了两个字符串 S 和 T,每个代表一个非负的有理数,如果他们相等的话,返回 True。有如下三种格式:
<IntegerPart>
(e.g. 0, 12, 123)<IntegerPart><.><NonRepeatingPart>
(e.g. 0.5, 1., 2.12, 2.0001)<IntegerPart><.><NonRepeatingPart><(><RepeatingPart><)>
(e.g. 0.1(6), 0.9(9), 0.00(1212))
Example 1: Input: S = "0.(52)", T = "0.5(25)" Output: true Explanation: Because "0.(52)" represents 0.52525252..., and "0.5(25)" represents 0.52525252525..... , the strings represent the same number. Example 2: Input: S = "0.1666(6)", T = "0.166(66)" Output: true Example 3: Input: S = "0.9(9)", T = "1." Output: true Explanation: "0.9(9)" represents 0.999999999... repeated forever, which equals 1. [See this link for an explanation.] "1." represents the number 1, which is formed correctly: (IntegerPart) = "1" and (NonRepeatingPart) = ""
数据规模:
- Each part consists only of digits.
- The
<IntegerPart>
will not begin with 2 or more zeros. (There is no other restriction on the digits of each part.) 1 <= <IntegerPart>.length <= 4
0 <= <NonRepeatingPart>.length <= 4
1 <= <RepeatingPart>.length <= 4
题解:我们可以考虑把整个字符串转换成double,如果没有循环节的话直接调用库函数 stod,如果有循环节的话,扩展一下循环节。因为循环节的长度最长也才4位,它可能有 1,2, 3, 4 这么长,所以我们可以拓展循环节到12位长度,这样不管它是1,2,3,4最后两个数都能拓展成一样的。
1 class Solution { 2 public: 3 bool isRationalEqual(string S, string T) { 4 return abs(StrToDouble(S) - StrToDouble(T)) < 1e-10; 5 } 6 double StrToDouble(string s) { 7 auto pos = s.find('('); 8 if (pos == string::npos) { 9 return stod(s); 10 } 11 string base = s.substr(0, pos), repeat = s.substr(pos + 1, s.size() - 1 - pos - 1); 12 while (base.size() < 20) { 13 base += repeat; 14 } 15 return stod(base); 16 } 17 };
Contest 119(2019年1月13日)(题号973-976)
链接:https://leetcode.com/contest/weekly-contest-119
总结:3/4,rank:1024/3847 第二题超时了好几次,第三题有个负数没有考虑。第三题也想了挺久的。orz
【973】K Closest Points to Origin(第一题 3分)
给了一堆坐标,返回离原点最近的 K 个坐标。
题解:直接排序,返回前 K 个。
1 class Solution { 2 public: 3 vector<vector<int>> kClosest(vector<vector<int>>& points, int K) { 4 const int n = points.size(); 5 if (n <= K) { 6 return points; 7 } 8 vector<pair<int, vector<int>>> dis(n); 9 for (int i = 0; i < n; ++i) { 10 auto p = points[i]; 11 int d = p[0] * p[0] + p[1] * p[1]; 12 dis[i] = make_pair(d, p); 13 } 14 sort(dis.begin(), dis.end()); 15 vector<vector<int>> ans(K); 16 for (int i = 0; i < K; ++i) { 17 ans[i] = dis[i].second; 18 } 19 return ans; 20 } 21 };
【976】Largest Perimeter Triangle(第二题 4分)
给了一个数组 A 代表边长,求能组成周长最长的三角形的周长。
数据规模:
3 <= A.length <= 10000
1 <= A[i] <= 10^6
题解:先排序,从大到小排序。第一个能组成的三角形就是所求的,而且这三个边长的变量元素一定是相邻的三个元素。想象一下如果相邻的第三个元素不能和前两个元素组成三角形,那么后面的元素肯定小于等于第三个元素,更加不能组成三角形。
1 class Solution { 2 public: 3 int largestPerimeter(vector<int>& A) { 4 const int n = A.size(); 5 sort(A.begin(), A.end(), cmp); 6 int ans = 0; 7 for (int i = 0; i < n - 2; ++i) { 8 int l1 = A[i], l2 = A[i+1], l3 = A[i+2]; 9 if (l2 + l3 <= l1) {continue;} 10 if (l1 - l2 >= l3 || l1 - l3 >= l2 || l2 - l3 >= l1) {continue;} 11 ans = l1 + l2 + l3; 12 if (ans > 0) { 13 return ans; 14 } 15 } 16 return ans; 17 } 18 static bool cmp(const int& a, const int& b) { 19 return a > b; 20 } 21 };
【974】Subarray Sums Divisible by K(第三题 6分)
【975】Odd Even Jump(第四题 8分)
Contest 120(2019年1月20日)(题号977-980)
结果:3/4,rank:557 / 3876。做了第一题,第二题,第四题,第三题差点就做出来了,orz。
链接:https://leetcode.com/contest/weekly-contest-120
【977】Squares of a Sorted Array(第一题 2分)
给了一个排序数组,里面有正有负,返回这个数组的平方数组,需要排序排好。
Example 1: Input: [-4,-1,0,3,10] Output: [0,1,9,16,100]
题解:我是先用了一个 lower_bound 找到了 0 的位置,然后 2 pointers,一个指向正数往大了走,一个指向负数往小了走。
1 class Solution { 2 public: 3 vector<int> sortedSquares(vector<int>& A) { 4 const int n = A.size(); 5 auto iter = lower_bound(A.begin(), A.end(), 0); 6 vector<int> ret(n); 7 if (iter == A.begin() || iter == A.end()) { 8 for (int i = 0; i < n; ++i) { 9 ret[i] = A[i] * A[i]; 10 } 11 if (iter == A.end()) { 12 reverse(ret.begin(), ret.end()); 13 } 14 } else { 15 int p1 = distance(A.begin(), iter), p2 = p1 - 1; //++p1, --p2 16 int cnt = 0; 17 while (p1 < n && p2 >= 0) { 18 if (abs(A[p2]) < abs(A[p1])) { 19 ret[cnt++] = A[p2] * A[p2]; 20 --p2; 21 } else { 22 ret[cnt++] = A[p1] * A[p1]; 23 ++p1; 24 } 25 } 26 while (p1 < n) { 27 ret[cnt++] = A[p1] * A[p1]; 28 ++p1; 29 } 30 while (p2 >= 0) { 31 ret[cnt++] = A[p2] * A[p2]; 32 --p2; 33 } 34 } 35 return ret; 36 } 37 };
【978】Longest Turbulent Subarray(第二题 5分)
找出最长的连续子数组,子数组中的元素满足如下性质之一,
- For
i <= k < j
,A[k] > A[k+1]
whenk
is odd, andA[k] < A[k+1]
whenk
is even; - OR, for
i <= k < j
,A[k] > A[k+1]
whenk
is even, andA[k] < A[k+1]
whenk
is odd.
返回最长子数组的长度。
题解:我们求原数组的差分数组,diff[i] = A[i]-A[i-1], 然后只保留正负号(正数是1,负数是-1)。我们用dp求最长的连续子数组,dp[i] 代表以 i 为结尾的 diff 数组的最长的连续正负子序列的长度。
if (diff[i] * diff[i-1] == -1) { dp[i] = dp[i-1]+1; } else { dp[i] = 1; }
1 class Solution { 2 public: 3 int maxTurbulenceSize(vector<int>& A) { 4 const int n = A.size(); 5 if (n <= 1) { 6 return n; 7 } 8 vector<int> diff(n, 0); 9 for (int i = 1; i < n; ++i) { 10 diff[i] = A[i] - A[i-1]; 11 if (diff[i] < 0) { 12 diff[i] = -1; 13 } else if (diff[i] > 0) { 14 diff[i] = 1; 15 } 16 } 17 // vector<int> dp(n, 1); 18 // dp[0] = 0; 19 int cur = 1, pre = 1; 20 int ret = 1; 21 for (int i = 2; i < n; ++i) { 22 if (diff[i] * diff[i-1] == -1) { 23 //dp[i] = dp[i-1] + 1; 24 cur = pre + 1; 25 ret = max(ret, cur); 26 } else { 27 cur = 1; 28 } 29 pre = cur; 30 } 31 return ret + 1; 32 } 33 };
【979】Distribute Coins in Binary Tree(第三题 6分)
给了一棵二叉树,上面有 N 个节点,N 个节点上面有 N 个coin,我们把 一个节点上的一个硬币移动到它的儿子节点或者父节点,称为一个 move,问把 N 个节点上每个节点上都有一个 coin,问需要多少步数。
题解:我比赛的时候先序遍历,中序遍历都想了,就是没想后序遍历,后来想到了,结果就差一点点ac了,尴尬。
我们可以这么思考,如果一个节点是叶子节点,它上面有三个硬币,那么它可以自己留一个,上交给爸爸两个,返回2。如果它上面有1个硬币,它就可以自己留着一个,不上交爸爸,返回0。如果它上面没有硬币,它就可以找它爸爸要一个,返回-1。
爸爸接到了儿子的信息,它要修改下自己的数量。儿子上交了,它就加上,儿子找它要,它就先给它儿子。然后我们用每次要的数量的绝对值累加步数。dfs一下,结果就呼之欲出了。
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 int moves = 0; 13 int distributeCoins(TreeNode* root) { 14 if (!root) { return 0; } 15 dfs(root); 16 return moves; 17 } 18 int dfs(TreeNode* root) { //return states: 0->no need, -3->need 3 coins, +3->give 3 coins 19 if (!root) {return 0;} 20 if (!root->left && !root->right) { 21 return root->val - 1; 22 } 23 int l = dfs(root->left); 24 root->val += l; 25 moves += abs(l); 26 int r = dfs(root->right); 27 root->val += r; 28 moves += abs(r); 29 return root->val - 1; 30 } 31 };
【980】Unique Paths III(第四题 7分)
给了一个2D grid,上面 1 代表起点,2代表重点,0代表可以走的路,-1代表障碍。题目要求我们从起点开始,遍历过所有能走的路,走到终点。求一共有多少中走法。
1 <= grid.length * grid[0].length <= 20
题解:我直接dfs了,数据规模很小。此外本题还可以状压dp,昨天被旅行商问题搞出了心理阴影,所以,今天先不想状压dp了。orz
1 class Solution { 2 public: 3 int visTot = 0, obs = 0; 4 int n, m, total; 5 int ret = 0; 6 vector<int> begin{-1, -1}, end{-1, -1}; 7 vector<int> dirx = {-1, 0, 1, 0}; 8 vector<int> diry = {0, -1, 0, 1}; 9 int uniquePathsIII(vector<vector<int>>& grid) { 10 n = grid.size(), m = grid[0].size(); 11 for (int i = 0; i < n; ++i) { 12 for (int j = 0; j < m; ++j) { 13 if (grid[i][j] == 1) { 14 begin = {i, j}; 15 } else if (grid[i][j] == 2) { 16 end = {i, j}; 17 } else if (grid[i][j] == -1) { 18 obs++; 19 } 20 } 21 } 22 total = n * m - obs; 23 vector<vector<int>> visit = grid; 24 dfs(grid, visit, begin[0], begin[1]); 25 return ret; 26 } 27 void dfs(const vector<vector<int>>& grid, vector<vector<int>>& visit, int curx, int cury) { 28 if (curx == end[0] && cury == end[1] && visTot + 1 == total) { 29 ++ret; 30 } 31 visit[curx][cury] = 3; 32 visTot++; 33 for (int i = 0; i < 4; ++i) { 34 int newx = curx + dirx[i], newy = cury + diry[i]; 35 if (newx >= 0 && newx < n && newy >= 0 && newy < m && visit[newx][newy] != -1 && visit[newx][newy] != 3) { 36 dfs(grid, visit, newx, newy); 37 } 38 } 39 visit[curx][cury] = 0; 40 visTot--; 41 } 42 };