链接:https://leetcode.com/contest/weekly-contest-111
【941】Valid Mountain Array(第一题 3分)
判断一个数组是不是 Mountain 数组。 Mountain 数组的定义是
题解
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 };
比赛情况记录:这场有点像是贪心专题了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 };
比赛情况记录:结果: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分)
比赛情况记录:本周一直比较萎靡,唉,结果: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分)
【956】Tallest Billboard(第四题 8分)
比赛情况记录:本周比赛马马虎虎吧,第四题不会做比较尴尬。结果: 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代表囚室有人。每天囚室的人员分布都在变化。变化规则就是如下两条:
给了 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 };
链接: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分)
比赛情况记录:今天早上爬起来看群主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分)
链接: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) = ""
数据规模:
<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 };
链接: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分)
结果: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分)
找出最长的连续子数组,子数组中的元素满足如下性质之一,
i <= k < j
, A[k] > A[k+1]
when k
is odd, and A[k] < A[k+1]
when k
is even;i <= k < j
, A[k] > A[k+1]
when k
is even, and A[k] < A[k+1]
when k
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 };
【Leetcode周赛】从contest-111开始。(一般是10个contest写一篇文章)
原文:https://www.cnblogs.com/zhangwanying/p/9998412.html