今天的比賽的題目相對來講比較「直白」,不像前幾周都是一些特定的算法,若是你沒學過不可能想出來。算法
作了這些周,對leetcode比賽的題目也發現了一些「規律」。 通常前兩道題都很「簡單」,只要有想法,直接敲代碼就能解出來。更多考察的是結果是否正確,速度其次。數組
後兩道題有些難度 ,不一樣場次難度不同,也可能和不一樣人的水平感覺不一樣。可是確定比前兩道要難。spa
通常在作後兩道題的時候,只要複雜度是對的,一些細節也不用考慮太多。例如數組開的空間大小,一些線性的提早剪枝判斷,寫不寫均可以過。最主要的是複雜度是同一個量級的。設計
相信leetcode這麼設計是爲了「人性化」,讓選手更關注比賽題目核心,可以在一個半小時內完成比賽題目。code
總之leetcode的比賽仍是很人性化,很注重主要考點,不糾結於細節。利用這些特性,能夠在比賽中排除一些錯誤想法。排序
下面是詳細的題解和思考。遊戲
比賽的地址 Weekly Contest 137element
https://leetcode-cn.com/contest/weekly-contest-137leetcode
1. 最後一塊石頭的重量
題目:rem
最後一塊石頭的重量(Last Stone Weight)
地址:
https://leetcode-cn.com/contest/weekly-contest-137/problems/last-stone-weight/
題意:
有一堆石頭,每塊石頭的重量都是正整數。
每一回合,從中選出兩塊最重的石頭,而後將它們一塊兒粉碎。假設石頭的重量分別爲 x 和 y,且 x <= y。那麼粉碎的可能結果以下:
若是 x == y,那麼兩塊石頭都會被徹底粉碎; 若是 x != y,那麼重量爲 x 的石頭將會徹底粉碎,而重量爲 y 的石頭新重量爲 y-x。 最後,最多隻會剩下一塊石頭。返回此石頭的重量。若是沒有石頭剩下,就返回 0。
思路:
一個數組,每次把最大的兩個數拿出來相減,而後把絕對值放回原數組。一直重複到最後只剩下一個元素,輸出便可。
典型的模擬題,按照題目的意思寫便可。能夠用堆來實現,每次拿堆頂的兩個最大元素。
因爲是第一題,每次都排序一遍,也能經過。不過在平常工程中,仍是老老實實用堆來實現吧。
class Solution { public: int lastStoneWeight(vector<int>& stones) { priority_queue< int > q; for(auto &stone : stones) { q.push(stone); } while(q.size()>1) { int x = q.top(); q.pop(); int y = q.top(); q.pop(); int z = abs(x-y); q.push(z); } return q.top(); } };
2. 刪除字符串中的全部相鄰重複項
題目:
刪除字符串中的全部相鄰重複項(Remove All Adjacent Duplicates In String)
地址:
題意:
給出由小寫字母組成的字符串 S,重複項刪除操做會選擇兩個相鄰且相同的字母,並刪除它們。
在 S 上反覆執行重複項刪除操做,直到沒法繼續刪除。
在完成全部重複項刪除操做後返回最終的字符串。答案保證惟一。
思路:
相似於遊戲「愛消除」,相同的兩個字母抵消掉,造成的新字符串再接着抵消,直到穩定爲止。
用棧來實現,遍歷字符串的每一個字符。若是棧爲空,則插入字符,不然比較字符和棧頂元素,相同則彈出棧頂元素,不一樣則壓棧。
最後輸出棧內的字符串便可。
代碼:
class Solution { public: string removeDuplicates(string S) { stack<char> st; for(auto ch : S) { if(st.empty()) { st.push(ch); } else { if(st.top()==ch) { st.pop(); } else { st.push(ch); } } } string res; while(!st.empty()) { res.push_back(st.top()); st.pop(); } reverse(res.begin(), res.end()); return res; } };
3. 最長字符串鏈
題目:
最長字符串鏈(Longest String Chain)
地址:
https://leetcode-cn.com/contest/weekly-contest-137/problems/longest-string-chain/
題意:
給出一個單詞列表,其中每一個單詞都由小寫英文字母組成。
若是咱們能夠在 word1 的任何地方添加一個字母使其變成 word2,那麼咱們認爲 word1 是 word2 的前身。例如,"abc" 是 "abac" 的前身。
詞鏈是單詞 [word_1, word_2, ..., word_k] 組成的序列,k >= 1,其中 word_1 是 word_2 的前身,word_2 是 word_3 的前身,依此類推。
從給定單詞列表 words 中選擇單詞組成詞鏈,返回詞鏈的最長可能長度。
思路:
這道題本質是圖算法。
分兩步解:
第一步先構造出每一個單詞之間的關係,判斷任意兩個單詞是爲前身後繼關係。構造完關係就能畫出了圖。
第二步就是求解這個圖中最長路徑。因爲是單向有向圖,並且沒有環。
構造一個集合,每次給集合放入新的點A,都判斷集合中其餘的點到該點的距離,取最大值爲集合內部到新點A的最大距離L。下次再加入新的點A1,若是A和A1連通,則集合到A1的距離爲L+1。
因爲終點有多個,最後要遍歷全部點的最長距離。
其實這道題的思想和Dijkstra算法是同樣的。
代碼:
class Solution { public: bool canChange(string& s1, string& s2) { int len1 = s1.length(); int len2 = s2.length(); if(len1+1!=len2) return false; int i=0; int j=0; while(j<len2) { if(s1[i]==s2[j]) { ++i; ++j; } else { ++j; if(j-i>1) { return false; } } } return true; } int longestStrChain(vector<string>& words) { int n = words.size(); vector<vector<int>> g(n, vector<int>(n, 0)); sort(words.begin(), words.end(), [](string& w1, string& w2) { return w1.length()<w2.length(); } ); for(int i = 0; i < n; ++i) { for(int j = i+1; j < n; ++j) { if(canChange(words[i], words[j])) { g[i][j] = 1; } } } vector<int> lcnt(n, 1); for(int i=0;i<n;++i) { for(int j=0;j<i;++j) { if(g[j][i]) { int tmp = lcnt[j]+1; lcnt[i] = max(tmp, lcnt[i]); } } } return *max_element(lcnt.begin(), lcnt.end()); } };
4. 最後一塊石頭的重量 II
題目:
最後一塊石頭的重量 II(Last Stone Weight II)
地址:
https://leetcode-cn.com/contest/weekly-contest-137/problems/last-stone-weight-ii/
題意:
有一堆石頭,每塊石頭的重量都是正整數。
每一回合,從中選出任意兩塊石頭,而後將它們一塊兒粉碎。假設石頭的重量分別爲 x 和 y,且 x <= y。那麼粉碎的可能結果以下:
若是 x == y,那麼兩塊石頭都會被徹底粉碎; 若是 x != y,那麼重量爲 x 的石頭將會徹底粉碎,而重量爲 y 的石頭新重量爲 y-x。 最後,最多隻會剩下一塊石頭。返回此石頭最小的可能重量。若是沒有石頭剩下,就返回 0。
思路:
和第一題的題意只有一句差異,就是每次拿石頭是「任意」的。問最後能消掉剩餘的最小值是多少。
通常最開始可能想到用貪心,但實際上沒有這種算法的。
因爲石頭碎掉以後還能放回去,相似於把石頭分紅兩堆來看。只要依次拿兩堆的石頭互相粉碎,最後剩下的就是最小整數。
最多有100個石頭,每一個石頭最多300的重量。因此兩個集合最大的差值不會超過30000。
用數組構造結果。
在加入第n個石頭重量爲m時,查找n-1個石頭可以組成的兩堆石頭的差值的絕對值爲diff。
該石頭兩個選擇,放入多的堆,則差值更大,爲diff+m; 放入小的堆,則差值爲|diff-m|。這時更新n個石頭能組成的全部重量。
最後輸出最後一個石頭能組成的最小重量便可。
代碼:
class Solution { public: int lastStoneWeightII(vector<int>& stones) { int diff[101][30001]={0}; int sum = 0; int n = stones.size(); for(int i=0;i<n;++i) { sum+=stones[i]; } diff[0][stones[0]] = 1; for(int i=1;i<n;++i) { for(int j=0;j<=sum;++j) { if(diff[i-1][j]) { diff[i][j+stones[i]] = 1; diff[i][abs(j-stones[i])] = 1; } } } for(int i = 0; i <= sum; ++i) { if(diff[n-1][i]) { return i; } } return 0; } };