LeetCode 力扣周赛 264

132 阅读2分钟

昨晚还和老婆说,周赛一定会打的不错。果然今天就进前一百啦哈哈哈。

周赛传送门

5906. 句子中的有效单词数

思路:字符串分割,模拟

时间复杂度O(n)O(n)

虽然是第一题,写起来还挺麻烦的。不过思路很简单,按空格分割,然后逐个单词检查即可。

class Solution {
public:
    bool check(const std::string &s, int l, int r) {
        if (l+1 >= r) {
            return false;
        }
        int cnt0 = 0, cnt1 = 0;
        for (int i = l+1; i < r; i++) {
            if (!(('a' <= s[i] && s[i] <= 'z') || s[i] == '-' || s[i] == '!' || s[i] == ',' || s[i] == '.')){
                return false;
            }
            if (s[i] == '-') {
                cnt0++;
                if (i == l+1 || i == r-1) {
                    return false;
                }
                if (!('a' <= s[i-1] && s[i-1] <= 'z')) {
                    return false;
                }
                if (!('a' <= s[i+1] && s[i+1] <= 'z')) {
                    return false;
                }
            }
            if (s[i] == '!' || s[i] == ',' || s[i] == '.') {
                cnt1++;
                if (i != r-1) {
                    return false;
                }
            }
        }
        if (cnt0 > 1 || cnt1 > 1) {
            return false;
        }
        return true;
    }
    int countValidWords(string s) {
        s += " "; // 在最后添加一个空格,避免最后一个单词处理不到。
        int anw = 0;
        for (int i = 0, pre = -1; i < s.size(); i++) {
            if (s[i] == ' ') {
                    if (check(s, pre, i)) {
                        anw ++;
                    }
                pre = i;
            }
        }
        return anw;
    }
};

5907. 下一个更大的数值平衡数

思路:预处理,暴力枚举

时间复杂度O(nl)O(n*l)ll 为数字长度。

不难发现,发现 1e6 的下一个数值平均数为 1224444。因此,可在 O(nl)O(n*l) 的时间复杂度下预处理出所有的 n[1,1e6]n\in [1,1e6] 的答案。

int anw[1000001] = {0};
class Solution {
public:
    Solution() {
        if (anw[1000000] == 0) {
            anw[1000000] = 1224444;
            for (int i = 1000000; i >= 1; i--) {
                if (check(i)) {
                    anw[i-1] = i;
                } else {
                    anw[i-1] = anw[i];
                }
            }
        }
    }
    bool check(int n) {
        int cnt[10] = {0};
        while(n > 0) {
            cnt[n%10]++;
            n /= 10;
        }
        for (int i = 0; i < 10; i++) {
            if (cnt[i] != i && cnt[i] != 0) {
                return false;
            }
        }
        return true;
    }
    int nextBeautifulNumber(int n) {
        return anw[n];
    }
};

5908. 统计最高分的节点数目

思路:DFS统计子树的节点数

时间复杂度O(n)O(n)

首先利用 DFS 在 O(n)O(n) 的时间复杂度内统计出所有子树的节点数量。

当我们将节点 ii 移除之后,整棵树最多会分为三部分:

  • 节点 ii 的左子树,记为 PlP_l
  • 节点 ii 的右子树,记为 PrP_r
  • 其他节点组成的一棵树,记为 PoP_o

其中,PlP_lPrP_r 已经在 DFS 中得到,Po=n1PlPrP_o = n - 1 - P_l - P_r。则节点 ii 的分数为

Si=max(1,Pl)max(1,Pr)max(1,Po)S_i = max(1, P_l) * max(1, P_r) * max(1,P_o)
class Solution {
public:
    int cnt[100000] = {0};
    vector<int> edge[100000];
    void dfs(int root) {
        // cnt[i] 记录了以 i 为根的节点数量
        
        cnt[root] = 1; // 先加上节点 i 自己
        for (int i = 0; i < edge[root].size(); i++) {
            int v = edge[root][i];
            dfs(v);
            cnt[root] += cnt[v]; // 累加上所有子树的
        }
    }
    int countHighestScoreNodes(vector<int>& p) {
        // 构造边表
        for (int i = 0; i < p.size(); i++) {
            if (p[i] != -1) {
                edge[p[i]].emplace_back(i);
            }
        }
        // dfs(i) 会得到以 i 根的子树的节点数量
        dfs(0);
        int64_t score = -1, anw = 0;
        for (int i = 0; i < p.size(); i++) {
            // 这里其实未区分左右子树,只判断有几棵子树。
            int64_t pl = 0, pr = 0, po = 0;
            if (edge[i].size() >= 1) {
                pl = cnt[edge[i][0]];
            }
            if (edge[i].size() >= 2) {
                pr = cnt[edge[i][1]];
            }
            po = p.size() - 1 - pl -pr;
            int64_t s = max(1L, pl) * max(1L, pr) * max(1L, po);
            if (s > score) {
                score = s, anw = 1;
            } else if (s == score) {
                anw++;
            }
        }
        return anw;
    }
};

5909. 并行课程 III

思路:记忆化搜索求解最长路径

时间复杂度O(n+e)O(n+e)

不难发现,本题是让求 DAG 上的最长路径,路径的长度即为路径上所有点的权值之和。

不妨先定义一个一维数组 costcostcosticost_i 表示以节点 ii 为起点的最长路径的长度:

  • 当节点 ii 无出边时,costi=timeicost_i = time_i
  • 当节点 ii 有出边时,
costi=max存在一条边ivcostv+timeicost_i = \max_{存在一条边 i→v }cost_{v} + time_i

最后,最大的 costicost_i 即为答案。

class Solution {
public:
    // 边表
    vector<int> edge[50001];
    // cost[i] 表示从 i 出发到其后继节点中最远节点的距离
    int cost[50001];
    void dfs(int root, const vector<int> &time) {
        // 如果 cost[root] != INT_MAX,说明一件计算过啦,无需再算。
        if (cost[root] != INT_MAX) {
            return;
        }
        int m = 0;
        // 计算后继节点的 cost,并记录最大值至 m.
        for (auto next : edge[root]) {
            dfs(next, time);
            if (m < cost[next]) {
                m = cost[next];
            }
        }
        // 因为该题中都是点权,所以 m 加上 root 节点的权值,即为 cost[root]
        cost[root] = m + time[root-1];
    }
    int minimumTime(int n, vector<vector<int>>& r, vector<int>& time) { 
        // 构造边表
        for (const auto &e : r) {
            edge[e[0]].push_back(e[1]);
        }
        // 初始化
        for (int i = 1; i <= time.size(); i++) {
            cost[i] = INT_MAX;
        }
        // 计算所有的 cost[i]
        for (int i = 1; i <= time.size(); i++) {
            dfs(i, time);
        }
        // anw 为答案,即cost[i] 最大的那个。
        int anw = 0;
        for (int i = 1; i <= time.size(); i++) {
            if (cost[i] > anw) {
                anw = cost[i];
            }
        }
        return anw;
    }
};