Leetcode 第189 场周赛题解

5412. 在既定时间做作业的学生人数

  • 类型:暴力枚举
  • 时间复杂度:O(N)。N 为区间数量。

因为对于每组 startTime 和 endTime 只查询一次,所以也没啥可优化的点,直接暴力枚举所有的 [startTime[i], endTime[i]],判断queryTime 是否在这个区间内即可。

class Solution {
public:
    int busyStudent(vector<int>& startTime, vector<int>& endTime, int queryTime) {
        int cnt = 0;
        for(int i = 0; i < startTime.size(); i++) {
            if(startTime[i] <= queryTime && queryTime <= endTime[i]) {
                cnt++;
            }
        }
        return cnt;
    }
};

5413. 重新排列句子中的单词

  • 类型:稳定排序,字符串分割,大小写转换
  • 时间复杂度:O(L+n*logn)。L 为 text.size(),n 为 分割后的字符串数量。

首先将 text 按空格分割,将分割结果放入一个 vetor
分割步骤如下:

  • 预设 vector result,string part 分别用来存储最终结果和中间结果。
  • 从 0 到 text.size() 枚举 i 。(i 要枚举到 text.size(),是为了保证将最后一个字符串放入 result)
  • 如果 i != text.size() 且 text[i] 不是空格,则 part += text[i];i++。
  • 如果 i == text.size() 或 text[i] 是空格,则将 part 放入 result,然后清空 part; i++。

part 放入 result 时,可以判断下 part[0] 是否为大写字母。大小写转换公式如下:

  • 大写变小写:(part[0] -= ‘A’) += ‘a’
  • 小写变大写:(part[0] -= ‘a’) += ‘A’

接下来,将 result 按照题目要求排序。因为要求长度相同的元素保持原来的位置,即稳定排序,可以使用 std::stable_sort 代替常用的 std::sort。

排序结束后,将 result 中字符串用空格拼接即可。
最后将句子首字符转换为大写。

class Solution {
public:
    string arrangeWords(string text) {
        vector<string> data;
        string part;
        for(int i = 0, n = text.size(); i <= n; i++) {
            if(i == n || text[i] == ' ') {
                if(part.size() > 0) {
                    if('A' <= part[0] && part[0] <= 'Z') {
                        (part[0] -= 'A') += 'a';
                    }
                    data.push_back(part);
                    part = "";
                }
            } else {
                part += text[i];
            }
        }
        string anw;
        stable_sort(data.begin(), data.end(), [](const string &l, const string &r) -> bool {
            return l.size() < r.size();
        });
        for(int i = 0, n = data.size(); i < n; i++) {
            if(i == 0) {
                anw += data[i];
                (anw[0] -= 'a') += 'A';
            } else {
                anw += " ";
                anw += data[i];
            }
        }
        return anw;
    }
};

5414. 收藏清单

  • 类型:暴力枚举,unordered_set
  • 时间复杂度:O(n*n*m)。n 为 favoriteCompanies.size(),m 为 favoriteCompanies[i] 的平均长度。

为了方便,下文中将 favoriteCompanies 简称为 FC。

首先,定义一个与 FC 等长的 vector>,设为 DL。将 FC[i] 的元素插入到 DL[i] 中。设结果为 cnt,初始为 0。
然后,枚举 DL[i],判断DL[i] 是否为 DL[j] (i != j) 的子集。如果DL[i] 不是其他 DL[j] 的子集,则 cnt += 1。

判断过程:
有两个 unordered_set A, B,判断 A 是否为 B的子集。
子集定义:如果 A 的所有元素均在 B 中,则 A 为 B 的子集。
用迭代器 iter 枚举 A 的所有元素,如果对于每个 iter 都有 B.find(*iter) != B.end() ,那么 A 就是 B 子集。否则 A 不是 B 的子集。

class Solution {
public:
    bool check(const unordered_set<string> &l, const unordered_set<string> &r) {
        for(const auto &v : l) {
            if(r.find(v) == r.end()) {
                return true;
            }
        }
        return false;
    }
    vector<unordered_set<string>> data;
    vector<int> peopleIndexes(vector<vector<string>>& fc) {
        for(const auto &v : fc) {
            unordered_set<string> us;
            for(const auto &s : v) {
                us.insert(s);
            }
            data.push_back(std::move(us));
        }
        vector<int> res;
        for(int i = 0, n = fc.size(); i < n; i++) {
            bool flag = true;
            for(int j = 0; j < n && flag; j++) {
                if(i == j || check(data[i], data[j])) { continue; }
                flag = false;
            }
            if(flag) {
                res.push_back(i);
            }
        }
        return res;
    }
};

5415. 圆形靶内的最大飞镖数量

  • 类型:计算几何
  • 时间复杂度:O(n^3)。n 为点的个数。

首先来思考一个问题,如果一个圆 C 能覆盖点集 S,当S包含两个及以上数量的点时,我们可以移动 C 找到一个 C’,使得 C’ 也能覆盖 S,且至少有两个点在 C’ 上。
Leetcode 第189 场周赛题解_第1张图片
为什么一定能通过平移和旋转C来找到C’呢?
首先所有的点都在 C 内,在移动过程中,必然会有点跑到 C 上。因为移动会导致点跑到C外面,但是一个点由圆内变为圆外时,必然要先从圆内变成圆上
Leetcode 第189 场周赛题解_第2张图片
设有两个不重合的点A,B。分别以 A, B 为圆心,r 为半径,画两个圆。

  • 若两个圆无交点,则说明A,B距离过远,不存在 C’。
  • 若两个圆有(一个或两个)交点,则存在C’。

设 A,B 连线的终点为 M。两圆的一个交点为P。设 MP 长度为d(可通过勾股定理求得)。另外,又知道MP的方向向量 dir 为(A.y-B.y,B.x-A.x)。所以:

P . x = d ∗ d i r . x / s q r t ( d i r . x 2 + d i r . y 2 ) + M . x P.x ={d*dir.x}/{sqrt(dir.x^2+ dir.y^2)} + M.x P.x=ddir.x/sqrt(dir.x2+dir.y2)+M.x
P . y = d ∗ d i r . y / s q r t ( d i r . x 2 + d i r . y 2 ) + M . y P.y ={d*dir.y}/{sqrt(dir.x^2+ dir.y^2)} + M.y P.y=ddir.y/sqrt(dir.x2+dir.y2)+M.y

那么接下来的问题就变成了,暴力枚举两个点 A,B,通过A,B 及确定的半径计算出 C’ 的圆心 center。然后再暴力枚举所有点,判断有多少点在以 center 为圆心的C’ 内。

#define N 101
#define LL long long
#define INF 0xfffffff
const double eps = 1e-8;
const double pi = acos(-1.0);
const double inf = ~0u>>2;
struct point {
    double x,y;
    point(double x = 0,double y =0 ):x(x),y(y){}
}p[N];

double dis(point a,point b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

int dcmp(double x) {
    if(fabs(x)<eps)return 0;
    else return x<0?-1:1;
}

point getcircle(point p1, point p2, double r) {
    point dir(p1.y-p2.y, p2.x-p1.x);
    point mid((p1.x+p2.x)/2, (p1.y+p2.y)/2);
    double d = sqrt(r*r-dis(p1,mid)*dis(p1,mid));
    double detX = d*dir.x/sqrt(dir.x*dir.x + dir.y*dir.y);
    double detY = d*dir.y/sqrt(dir.x*dir.x + dir.y*dir.y);
    return point(mid.x + detX, mid.y + detY);
}

class Solution {
public:
    
    int numPoints(vector<vector<int>>& points, int ir) {
        int n = points.size();
        for(int i = 1 ;i <= n; i++) {
            p[i].x = points[i-1][0];
            p[i].y = points[i-1][1];
        }
        int maxz = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 1 ; j <= n ;j++) {
                if(i == j || dcmp(dis(p[i],p[j])-2*ir)>0) {
                    continue;
                }
                int tmax = 0;
                point cir = getcircle(p[i],p[j], ir);
                for(int g = 1; g <= n ;g++)
                {
                    if(dcmp(dis(cir,p[g])-ir)>0)
                    continue;
                    tmax++;
                }
                maxz = max(maxz,tmax);
            }
        }
        return maxz;
    }
};

如果感觉有点意思,可以关注HelloNebula

  • 分享周赛题解
  • 分享计算机专业课知识
  • 分享C++相关岗位面试题
  • 分享专业书籍PDF

Leetcode 第189 场周赛题解_第3张图片

你可能感兴趣的:(题解给力)