【Leetcode周賽】從contest-111開始。(通常是10個contest寫一篇文章)

Contest 111 (題號941~944)(2019年1月19日,補充題解,主要是943題)

連接:https://leetcode.com/contest/weekly-contest-111node

【941】Valid Mountain Array(第一題 3分)(google tag)git

判斷一個數組是否是 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 大,而後想象兩撥人登山,這個定義就是看最後兩撥人是否是爬到了同一個山頂。ide

 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 };
View Code

 

Delete Columns to Make Sorted(第二題 4分)函數

DI String Match(第三題 5分)優化

【943】Find the Shortest Superstring(第四題 8分)ui

Given an array A of strings, find any smallest string that contains each string in A as a substring.this

We may assume that no string in A is substring of another string in A.google

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 };
View Code

 

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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

 

【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 };
bfs

 

【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 };
View Code

 

【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 };
View Code

 

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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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) = ""

數據規模:

  1. Each part consists only of digits.
  2. The <IntegerPart> will not begin with 2 or more zeros.  (There is no other restriction on the digits of each part.)
  3. 1 <= <IntegerPart>.length <= 4
  4. 0 <= <NonRepeatingPart>.length <= 4
  5. 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 };
View Code

 

 

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 };
View Code

 

【976】Largest Perimeter Triangle(第二題 4分)

給了一個數組 A 表明邊長,求能組成周長最長的三角形的周長。

數據規模: 

  1. 3 <= A.length <= 10000
  2. 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 };
View Code

  

【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 };
View Code

 

【978】Longest Turbulent Subarray(第二題 5分)

找出最長的連續子數組,子數組中的元素知足以下性質之一,

  • For i <= k < jA[k] > A[k+1] when k is odd, and A[k] < A[k+1] when k is even;
  • OR, for i <= k < jA[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 };
View Code

 

【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 };
View Code

 

【980】Unique Paths III(第四題 7分)

 給了一個2D grid,上面 1 表明起點,2表明重點,0表明能夠走的路,-1表明障礙。題目要求咱們從起點開始,遍歷過全部能走的路,走到終點。求一共有多少中走法。

  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 };
View Code
相關文章
相關標籤/搜索