Leetcode 第137場周賽解題報告

今天的比賽的題目相對來講比較「直白」,不像前幾周都是一些特定的算法,若是你沒學過不可能想出來。算法

作了這些周,對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)

地址:

https://leetcode-cn.com/contest/weekly-contest-137/problems/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;
    }
};
相關文章
相關標籤/搜索