排名 193。其实前三题手速还可以,第四题直接卡懵圈了,没有想出来排列方式,好恨哦。
5967. 检查是否所有 A 都在 B 之前
思路:子串判定
时间复杂度: O ( n ) \mathrel{O}(n) O(n)
空间复杂度: O ( 1 ) \mathrel{O}(1) O(1)
如有 ‘a’ 在 ‘b’ 之后的情形,则必有 “ba” 子串。反之,则必然没有。
class Solution {
public:
bool checkString(string s) {
return s.find("ba") == string::npos;
}
};
5968. 银行中的激光束数量
思路:模拟
时间复杂度: O ( n ∗ m ) \mathrel{O}(n*m) O(n∗m)
空间复杂度: O ( 1 ) \mathrel{O}(1) O(1)
按行遍历,记录上一个非空行中的激光束数量,然后和当前行的激光束两两连接。
class Solution {
public:
// 统计一行中激光束的数量
int count(const std::string &str) {
int cnt = 0;
for (auto c : str) {
cnt += (c == '1');
}
return cnt;
}
int numberOfBeams(vector<string>& bank) {
// pre 记录上一非空行的激光数量
int pre = 0;
// anw 记录答案
int anw = 0;
for (int i = 0; i < bank.size(); i++) {
// 统计当前行的激光束数量
int now = count(bank[i]);
if (now > 0) {
// 上一非空行和当前行两两连接,即做乘法。
anw += pre * now;
pre = now;
}
}
return anw;
}
};
5969. 摧毁小行星
思路:排序
时间复杂度: O ( n ∗ lg n ) \mathrel{O}(n*\lg n) O(n∗lgn)
空间复杂度: O ( 1 ) \mathrel{O}(1) O(1)
显然从小到大依次摧毁是最优策略。因此先升序排序,然后依次检查即可。
class Solution {
public:
bool asteroidsDestroyed(int m, vector<int>& ast) {
int64_t mass = m;
sort(ast.begin(), ast.end());
for (auto w : ast) {
if (mass < w) {
return false;
}
mass += w;
}
return true;
}
};
5970. 参加会议的最多员工数
思路:反向建图,判环
时间复杂度: O ( n ) \mathrel{O}(n) O(n)
空间复杂度: O ( n ) \mathrel{O}(n) O(n)
首先题目描述有个不严谨的地方:每位员工都有一位喜欢的员工,每位员工当且仅当他被安排在喜欢员工的旁边,他才会参加会议。
应该改为:每位员工都有一位喜欢的员工,每位员工当且仅当他被安排在喜欢员工的旁边并且他喜欢的员工参加会议,他才会参加会议。
安排座次有两种策略:
- 所有参会员工构成一个环(如下图中标黄的点)
- 所有参会员工排列在若干对互相喜欢的员工两边。如下图中标黄的点对,每个点均可延伸出一条链。
比赛时没想到第二种策略,止步三道题,真是太难过了。想清楚如何安排座次后,代码还是挺好写的。
对于策略一,可用一次 DFS 找出图中的最大环。
对于策略二,可先找出所有点数为二的环,对于每个环分别找出两个端点作为起点且不含另一点的最长链。
using namespace std;
class Solution {
vector<vector<int>> edges; // 边表
// 两个辅助召唤的数组,类似 tarjan 找有向图强连通分量的做法。
// status[i] = 0 表示以 i 为起点的dfs尚未开始
// status[i] = 1 表示以 i 为起点的dfs开始了,但还未结束;
// status[i] = 2 表示以 i 为起点的dfs结束了。
vector<int> status;
// 深度,即和 dfs 根节点的距离
vector<int> depth;
vector<pair<int, int>> pairs; // 所有点数为2的环
int seek_max_circle(int root, int d) {
depth[root] = d;
status[root] = 1;
int mc = 0;
for (auto next : edges[root]) {
if (status[next] == 0) {
mc = max(mc, seek_max_circle(next, d+1));
} else if (status[next] == 1) {
int c = d - depth[next] + 1;
mc = max(mc, c);
if (c == 2) {
pairs.push_back(make_pair(root, next));
}
}
}
status[root] = 2;
return mc;
}
int seek_link(int root, int pre, int depth) {
int max_d = depth;
// 每个点只会有一条入边,因此从 root 出发不会有环了
for (auto next : edges[root]) {
if (next != pre) {
max_d = max(max_d, seek_link(next, root, depth+1));
}
}
return max_d;
}
public:
int maximumInvitations(vector<int>& fav) {
edges.resize(fav.size());
status.resize(fav.size());
depth.resize(fav.size());
// 建图 fav[i] 指向 i
for (int i = 0; i < fav.size(); i++) {
edges[fav[i]].emplace_back(i);
}
// 策略一:找出最大的环
int max_circle = 0;
for (int i = 0; i < fav.size(); i++) {
if (status[i] == 0) {
max_circle = max(max_circle, seek_max_circle(i, 0));
}
}
// 策略二
int points = 0;
for (const auto &[u, v] : pairs) {
points += seek_link(u, v, 1) + seek_link(v, u, 1);
}
return max(max_circle, points);
}
};