LeetCode-contest記錄(雙週賽21&單週賽179)

寫在前面

週末兩天leetcode比賽做題整理。
歡迎大家訪問我的個人博客網站:flamsteed

雙週賽21

5336. 上升下降字符串

給你一個字符串 s ,請你根據下面的算法重新構造字符串:

從 s 中選出 最小 的字符,將它 接在 結果字符串的後面。

從 s 剩餘字符中選出 最小 的字符,且該字符比上一個添加的字符大,將它 接在 結果字符串後面。

重複步驟 2 ,直到你沒法從 s 中選擇字符。

從 s 中選出 最大 的字符,將它 接在 結果字符串的後面。

從 s 剩餘字符中選出 最大 的字符,且該字符比上一個添加的字符小,將它 接在 結果字符串後面。

重複步驟 5 ,直到你沒法從 s 中選擇字符。

重複步驟 1 到 6 ,直到 s 中所有字符都已經被選過。

在任何一步中,如果最小或者最大字符不止一個 ,你可以選擇其中任意一個,並將其添加到結果字符串。

請你返回將 s 中字符重新排序後的 結果字符串 。

示例 1:

輸入:s = “aaaabbbbcccc”

輸出:“abccbaabccba”

解釋:第一輪的步驟 1,2,3 後,結果字符串爲 result = “abc”

第一輪的步驟 4,5,6 後,結果字符串爲 result = “abccba”

第一輪結束,現在 s = “aabbcc” ,我們再次回到步驟 1

第二輪的步驟 1,2,3 後,結果字符串爲 result = “abccbaabc”

第二輪的步驟 4,5,6 後,結果字符串爲 result = “abccbaabccba”

解法一:比賽的時候沒想太多,用了模擬,很蠢,而且代碼量很大。

代碼:

class Solution {
public:
    int getMinidx(string& s){
        char c = s[0];
        int p = 0;
        for(int i=1; i<s.length(); i++){
            if(c>s[i]){
                c = s[i];
                p = i;
            }
        }
        return p;
    }
    int getMaxidx(string& s){
        char c = s[0];
        int p = 0;
        for(int i=1; i<s.length(); i++){
            if(c<s[i]){
                c = s[i];
                p = i;
            }
        }
        return p;
    }
    char getMin(string& s){
        char c = s[0];
        for(int i=1; i<s.length(); i++){
            if(c>s[i])
                c = s[i];
        }
        return c;
    }
    char getMax(string& s){
        char c = s[0];
        for(int i=1; i<s.length(); i++){
            if(c<s[i])
                c = s[i];
        }
        return c;
    }
    string sortString(string s) {
        string res = "";
        if(s.empty()) return res;
        while(true){
            //1
            char t = getMin(s);
            int p = getMinidx(s);
            res = res + t;
            s.erase(p,1);
            if(s.empty()) break;
            bool k = true;
            while(k){//2 3
                k = false;
                char m = s[0];
                int pp = 0;
                for(int i=1; i<s.length(); i++){
                    if(s[i]>res[res.length()-1]){
                        k = true;
                        m = s[i];
                        pp = i;
                        break;
                    }
                }
                if(k==false) break;
                for(int i=0; i<s.length(); i++){
                    if(s[i]>res[res.length()-1] && s[i]<m){
                        m = s[i];
                        pp = i;
                    }
                }
                res = res + m;
                s.erase(pp,1);
                if(s.empty()){
                    break;
                }
            }
            if(s.empty()) break;
            //4
            t = getMax(s);
            p = getMaxidx(s);
            res = res + t;
            s.erase(p,1);
            if(s.empty()) break;
            k = true;
            while(k){//5 6
                k = false;
                char m = s[0];
                int pp = 0;
                for(int i=1; i<s.length(); i++){
                    if(s[i]<res[res.length()-1]){
                        k = true;
                        m = s[i];
                        pp = i;
                        break;
                    }
                }
                if(k==false) break;
                for(int i=0; i<s.length(); i++){//5
                    if((s[i]<res[res.length()-1]) && (s[i]>m)){
                        m = s[i];
                        pp = i;
                    }
                }
                res = res + m;
                s.erase(pp,1);
                if(s.empty()){
                    break;
                }
            }
            if(s.empty()){
                break;
            }
        }
        return res;
    }
};

解法二:用26位的數組存放字母個數,然後從前往後,從後往前加減,答案就出來了,比模擬不知道好到哪裏去了。

代碼:

class Solution {
public:
    string sortString(string s) {
        if (s.empty()) return s;
        vector<int> a(26,0);
        for(int i=0; i<s.length(); i++){
            a[int(s[i]-'a')]++;
        }
        string res = "";
        do{
            for(int i=0; i<26; i++){//1 2 3
                if(a[i]>0){
                    res = res + char(i+int('a'));
                    a[i]--;
                }
            }
            for(int i=25; i>=0; i--){//4 5 6
                if(a[i]>0){
                    res = res + char(i+int('a'));
                    a[i]--;
                }
            }
        }while(res.length()<s.length());
        return res;
    }
};

5337. 每個元音包含偶數次的最長子字符串

給你一個字符串 s ,請你返回滿足以下條件的最長子字符串的長度:每個元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出現了偶數次。

示例 1:

輸入:s = “eleetminicoworoep”

輸出:13

解釋:最長子字符串是 “leetminicowor” ,它包含 e,i,o 各 2 個,以及 0 個 a,u 。

提示:

1 <= s.length <= 5 x 10^5

s 只包含小寫英文字母。

解法:n^2暴搜肯定過不了,超過十萬位了,所以就不嘗試了,這題我是一點思路都沒有,看了題解還是一頭霧水,大部分解法是用bit mask,位操作解法都是非常巧,但是很難想。一共五個元音,2^5=32種狀態,用32個整數表示就完事了,然後相同的狀態重複出現,就是出現了答案,比較出最大值就行了。(太巧秒了orz)

代碼:

class Solution {
public:
    int findTheLongestSubstring(string s) {
        vector<int> pre(32,INT_MAX);
        pre[0]=-1;
        const int N=s.size();
        int cur=0;
        int ans=0;
        for(int i=0;i<N;++i){
            switch(s[i]){//狀態壓縮
                case 'a':cur^=1;break;
                case 'e':cur^=2;break;
                case 'i':cur^=4;break;
                case 'o':cur^=8;break;
                case 'u':cur^=16;break;
                default:break;
            }
            if(pre[cur]==INT_MAX) pre[cur]=i;//未記錄過就記錄位置
            else ans=max(ans,i-pre[cur]);//統計答案
        }
        return ans;
    }
};

5338. 二叉樹中的最長交錯路徑

給你一棵以 root 爲根的二叉樹,二叉樹中的交錯路徑定義如下:

選擇二叉樹中 任意 節點和一個方向(左或者右)。

如果前進方向爲右,那麼移動到當前節點的的右子節點,否則移動到它的左子節點。

改變前進方向:左變右或者右變左。

重複第二步和第三步,直到你在樹中無法繼續移動。

交錯路徑的長度定義爲:訪問過的節點數目 - 1(單個節點的路徑長度爲 0 )。

請你返回給定樹中最長 交錯路徑 的長度。

解法:數據範圍是最大50000個點,遞歸肯定爆,然後我還是寫了一個遞歸,然後超時了,其實遞歸稍微改一下就行了,自頂向下遍歷二叉樹,方向是對的就上層深度+1,不對就從1開始重新計算,這樣就不會超時了。

代碼:

class Solution {
public:
    int res = 0;
    void helper(TreeNode* root, int k, int d){
        if(root==NULL) return;
        res = max(res,d);
        if(k){
            helper(root->left,0,d+1);
            helper(root->right,1,1);
        }
        else{
            helper(root->left ,0,1);
            helper(root->right,1,d+1);
        }
    }
    int longestZigZag(TreeNode* root) {
        if(root==NULL) return 0;
        helper(root,0,0);
        helper(root,1,0);
        return res;
    }
};

5339. 二叉搜索子樹的最大鍵值和

給你一棵以 root 爲根的 二叉樹 ,請你返回 任意 二叉搜索子樹的最大鍵值和。

二叉搜索樹的定義如下:

任意節點的左子樹中的鍵值都小於此節點的鍵值。

任意節點的右子樹中的鍵值都大於此節點的鍵值。

任意節點的左子樹和右子樹都是二叉搜索樹。

解法:比賽的時候想都沒想,其實和上一題大同小異,這裏用vector作爲函數範圍值值得我學習,這樣dfs確實很方便,vector包含四個值{是否是搜索樹,子樹最小值,子樹最大值,當前的鍵值和},然後通過最大值和最小值來和當前的根比較,判斷是否是搜索樹,再統計和,就完事了。

代碼:

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
    int res = 0;
    vector<int> dfs(TreeNode* root){
        if(root==NULL) return {true, INT_MAX, INT_MIN, 0};

        auto lt = dfs(root->left);
        auto rt = dfs(root->right);

        int p = root->val;
        if(!lt[0] || !rt[0] || p<=lt[2] || p>=rt[1])
            return {false,0,0,0};
        
        int sum(0),currmin,currmax;
        currmin = root->left ? root->left->val : root->val;
        currmax = root->right? root->right->val : root->val;

        sum += root->val + lt[3] + rt[3];
        res = max(res,sum);
        return {true, currmin, currmax, sum};
    }
    int maxSumBST(TreeNode* root) {
        auto k = dfs(root);
        return res;
    }
};

周賽 179

5352. 生成每種字符都是奇數個的字符串

給你一個整數 n,請你返回一個含 n 個字符的字符串,其中每種字符在該字符串中都恰好出現 奇數次 。

返回的字符串必須只含小寫英文字母。如果存在多個滿足題目要求的字符串,則返回其中任意一個即可。

解法:全部填a,偶數的話最後一位填b。

代碼:

class Solution {
public:
    string generateTheString(int n) {
        string s = "";
        if(n==0) return s;
        if(n%2==1){
            for(int i=1; i<=n; i++)
                s = s + 'a';
        }
        else{
            for(int i=1; i<n; i++)
                s = s + 'a';
            s = s + 'b';
        }
        return s;
    }
};

5353. 燈泡開關 III

房間中有 n 枚燈泡,編號從 1 到 n,自左向右排成一排。最初,所有的燈都是關着的。

在 k 時刻( k 的取值範圍是 0 到 n - 1),我們打開 light[k] 這個燈。

燈的顏色要想 變成藍色 就必須同時滿足下面兩個條件:

燈處於打開狀態。

排在它之前(左側)的所有燈也都處於打開狀態。

請返回能夠讓所有開着的燈都變成藍色的時刻數目 。

解法:先用模擬做了一遍,然後爆了,然後細細一想,其實藍色燈的意思就是:第i個時間,1~i的燈都開了,這時候纔是全藍,求和判斷就完事了。

代碼:

class Solution {
public:
    int numTimesAllBlue(vector<int>& light) {
        if(light.empty()) return 0;
        int res = 0;
        int l = light.size();
        long s = 0;
        for(int i=0; i<l; i++){
            s += light[i];
            if(s == (long(1+i+1))*(long(i+1))/2) res++;
        }
        return res;
    }
};

5355. T 秒後青蛙的位置

給你一棵由 n 個頂點組成的無向樹,頂點編號從 1 到 n。青蛙從 頂點 1 開始起跳。規則如下:

在一秒內,青蛙從它所在的當前頂點跳到另一個未訪問過的頂點(如果它們直接相連)。

青蛙無法跳回已經訪問過的頂點。

如果青蛙可以跳到多個不同頂點,那麼它跳到其中任意一個頂點上的機率都相同。

如果青蛙不能跳到任何未訪問過的頂點上,那麼它每次跳躍都會停留在原地。

無向樹的邊用數組 edges 描述,其中 edges[i] = [fromi, toi] 意味着存在一條直接連通 fromi 和 toi 兩個頂點的邊。

返回青蛙在 t 秒後位於目標頂點 target 上的概率。

解法:先從target倒着跳,求出跳到根的跳數,同時記錄每次跳到的位置,和時間比較一下,如果時間不夠就輸出0,

因爲青蛙是死腦筋,所以如果target不是葉節點,且時間大於跳數,那青蛙還是不會停在target,輸出0。

完成以上判斷之後,剩下的情況都能跳到,求概率就行了,這時候遍歷之前記錄的位置,統計這些位置的子樹個數,然後計算概率就行了。

代碼:

class Solution {
public:
    double frogPosition(int n, vector<vector<int>>& edges, int t, int target) {
        if(edges.empty()){
            if(t==0) return 0.0;
            if(target==1) return 1.0;
            return 0.0;
        }
        int d = 0;
        int l = 0;
        int r = target;
        vector<int> path;
        for(int i=0; i<edges.size(); i++){
            if(edges[i][0]>edges[i][1]){
                int t = edges[i][0];
                edges[i][0] = edges[i][1];
                edges[i][1] = t;
            }
        }
        bool f = true;
        if(target==1){
            f = true;
            for(int i=0; i<edges.size(); i++){
                if(edges[i][0]==target){
                    f = false;
                    break;
                }
            }
            if(!f && t>0) return 0.0;
            return 1.0;
        }
        while(l!=1 && f){
            f = false;
            for(int i=0; i<edges.size(); i++){
                if(edges[i][1]==r){
                    l = edges[i][0];
                    r = l;
                    path.push_back(l);
                    d++;
                    f = true;
                    break;
                }
            }
        }
        if(t<d || !f) {
            if(edges[0][0]==target) return 1.0;
            return 0.0;
        }
        else if(t>d){
            f = true;
            for(int i=0; i<edges.size(); i++){
                if(edges[i][0]==target){
                    f = false;
                    break;
                }
            }
            if(!f) return 0.0;
        }
        vector<int> num;
        for(int i=0; i<path.size(); i++){
            int s = 0;
            for(int j=0; j<edges.size(); j++){
                if(edges[j][0]==path[i]){
                    s++;
                }
            }
            num.push_back(s);
        }
        double res = 1.0;
        for(int i=0; i<num.size(); i++){
            res = res * (1.0)/double(num[i]);
        }
        return res;
    }
};

5354. 通知所有員工所需的時間

公司裏有 n 名員工,每個員工的 ID 都是獨一無二的,編號從 0 到 n - 1。公司的總負責人通過 headID 進行標識。

在 manager 數組中,每個員工都有一個直屬負責人,其中 manager[i] 是第 i 名員工的直屬負責人。對於總負責人,manager[headID] = -1。題目保證從屬關係可以用樹結構顯示。

公司總負責人想要向公司所有員工通告一條緊急消息。他將會首先通知他的直屬下屬們,然後由這些下屬通知他們的下屬,直到所有的員工都得知這條緊急消息。

第 i 名員工需要 informTime[i] 分鐘來通知它的所有直屬下屬(也就是說在 informTime[i] 分鐘後,他的所有直屬下屬都可以開始傳播這一消息)。

返回通知所有員工這一緊急消息所需要的 分鐘數 。

解法:因爲每一層的不同領導通知時間會不一樣,只有時間最長的纔是答案,所以用dfs是最合適的,考慮到有40000個點,直接搜肯定爆(然後我還是直接搜了一次),其實只需要做一點點優化就行了,提前用二維數組存下每個領導的手下,在dfs內直接遍歷對應的下屬數組就行了。

代碼:

class Solution {
public:
    int res = 0;
    void dfs(int headID, vector<vector<int>>& m, vector<int>& a, int t){
        if(m[headID].empty()){//到達最下層員工,返回答案。
            res = max(res,t);
            return;
        }
        t += a[headID];
        for(int i=0; i<m[headID].size(); i++){
            dfs(m[headID][i],m,a,t);
        }
    }
    int numOfMinutes(int n, int headID, vector<int>& manager, vector<int>& informTime) {
        vector<vector<int>> m(informTime.size()+1,vector<int>());
        for(int i=0; i<manager.size(); i++){//預處理領導的下屬。
            if(manager[i]!=-1)
                m[manager[i]].push_back(i);
        }
        dfs(headID,m,informTime,0);
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章