四道题都不难,中间两题卡了我比较长时间,幸亏最后一题是个单调栈一眼题,不然就要掉大分了
知识点:优先队列,差分数组,单调栈
检查是否所有字符出现次数相同
给定一个字符串 (s),如果 (s) 中所有字符串出现的次数一样,那么我们称之为好字符串,请返回 (s) 是否是好字符串
题解
把每一个字符出现的次数用桶维护,扔到集合里面去,利用集合的去重功能,判断集合的 size
是否为 (1)
class Solution {
public:
bool areOccurrencesEqual(string s) {
vector<int> a(26);
int n = s.length();
set<int> st;
for (int i = 0; i < n; ++i) a[s[i] - 'a']++;
for (int i = 0; i < 26; ++i)
if (a[i]) st.insert(a[i]);
return st.size() == 1;
}
};
最小未被占据椅子的编号
(n) 个人在办聚会,有无限个椅子,每个人有到达时间和离开时间,每个人到达聚会的时候会选择空闲的最小编号的椅子
现在给定 (n) 个人的到达时间和离开时间,计算出第 (t) 个人坐的椅子的编号
题解
用一个优先队列存储每个椅子的空闲时间和编号,用一个集合存储 (n) 个椅子的编号
遍历 (n) 个人,根据到达时间和离开时间处理空闲和忙碌的椅子,更新优先队列和集合即可
// cpp
struct node {
int idx, ed;
node() {}
node(int _idx, int _ed):
idx(_idx), ed(_ed) {}
};
bool operator< (const node& x, const node& y) {
if (x.ed == y.ed) return x.idx < y.idx;
return x.ed > y.ed;
}
bool cmp(vector<int> &x, vector<int> &y) {
if (x[0] == y[0]) return x[1] < y[1];
return x[0] < y[0];
}
class Solution {
public:
int smallestChair(vector<vector<int>>& t, int target) {
vector<int> vec;
int n = t.size();
for (int i = 0; i < n; ++i) vec.push_back(i), t[i].push_back(i);
set<int> st(vec.begin(), vec.end());
priority_queue<node> pq;
sort(t.begin(), t.end(), cmp);
int res = 0;
for (int i = 0; i < n; ++i) {
while (!pq.empty()) {
node temp = pq.top();
if (temp.ed <= t[i][0]) pq.pop(), st.insert(temp.idx);
else break;
}
res = *st.begin();
pq.push(node(res, t[i][1]));
st.erase(st.begin());
if (t[i][2] == target) break;
}
return res;
}
};
描述绘画结果
给定 (n) 个左闭右开区间 ([l, r)),并给这个区间上每一个值加上 (w)
返回操作后的数轴,要求将相同值写成左闭右开区间的形式,值为 (0) 的区间跳过
举例来讲,如果 (1, 2, 3) 的值都是 (12),并且 (4) 的值为 (13),那我们可以把其合并成 ([1, 4), [4, 5)),
注意,对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间
例如 ([1, 4), [4, 7)) 均为 (12),但前者为 (5 + 7),后者为 (3 + 9),此时我们不能将其合并
题解
原题题意比较冗余,也很难抽象题意描述,但是考点很简单:差分数组
差分数组说的是:做 (n) 次区间加法操作,可以用离线算法在 (O(n)) 时间得到更新后的区间
具体来讲,如果要给区间 ([l, r)) 统一加上 (w),只需要在 (l) 处加上 (w),在 (r) 处减去 (w),如此进行 (n) 次,最后对原数组进行前缀和,便可以得到更新后的数组,这里的「原数组」被称为差分数组
我们对更新后的数组进行划分,将相同的值归纳为一个区间即可
此题在差分数组的基础上多了一个要求:对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间
这个也比较好处理,利用贪心的思想,继续构造一个差分数组,区间加「区间右端点」的值即可,聪明的你能想明白贪心的正确性吗?
时间复杂度 (O(n))
// cpp
typedef long long LL;
class Solution {
public:
vector<vector<long long>> splitPainting(vector<vector<int>>& seg) {
int n = seg.size();
int maxx = 0;
for (int i = 0; i < n; ++i) {
maxx = max(maxx, seg[i][1]);
seg[i].push_back(i + 1);
}
vector<LL> vec1(maxx + 7);
vector<LL> vec2(maxx + 7);
for (int i = 0; i < n; ++i) {
int a = seg[i][0], b = seg[i][1], c = seg[i][2], d = seg[i][3];
vec1[a] += c, vec1[b] -= c;
vec2[a] += d, vec2[b] -= d;
}
for (int i = 1; i <= maxx; ++i) {
vec1[i] += vec1[i - 1];
vec2[i] += vec2[i - 1];
}
vector<vector<LL>> ans;
int a = 1, b = 1;
for (int i = 1; i <= maxx; ++i) {
if (vec1[i] != vec1[a] || vec1[i] == vec1[a] && vec2[i] != vec2[a]) {
if (!vec1[i - 1]) {a = b = i; continue;}
else {
ans.push_back(vector<LL>{a, i, vec1[i - 1]});
a = b = i;
}
}
}
return ans;
}
};
从队列中可以看到的人数
给定 (n) 个人及其身高 (h_{i})
对于两个人 (i, j),其中 (i) 在 (j) 左边,如果 (i, j) 中间的每一个人都没有他们高,那么 (i) 可以看到右边的 (j)
计算出每个人可以看到的右边的人数
数据规定
(1leq nleq 10^5)
题解
对于当前的 (i) 而言,我们可以计算出他左边第一个比他高的人 (L),以及右边第一个比他高的人 (R)
如果 (L) 存在,那么 (L) 可以看到 (i)
如果 (R) 存在,那么 (i) 可以看到 (R)
使用单调栈预处理即可,时间复杂度 (O(n))
// cpp
class Solution {
public:
vector<int> canSeePersonsCount(vector<int>& h) {
int n = h.size();
vector<int> rmax(n + 7);
vector<int> lmax(n + 7);
vector<int> ans(n + 7);
vector<int> a(n + 7);
for (int i = 1; i <= n; ++i) a[i] = h[i - 1];
stack<int> mono1, mono2; // 递增
for (int i = 1; i <= n; ++i) {
while (!mono1.empty() && a[mono1.top()] < a[i]) {
rmax[mono1.top()] = i;
mono1.pop();
}
mono1.push(i);
}
for (int i = n; i >= 1; --i) {
while (!mono2.empty() && a[mono2.top()] < a[i]) {
lmax[mono2.top()] = i;
mono2.pop();
}
mono2.push(i);
}
for (int i = 1; i <= n; ++i) {
int L = lmax[i], R = rmax[i];
if (L >= 1 && L <= n) ans[L]++;
if (R >= 1 && R <= n) ans[i]++;
}
return {ans.begin() + 1, ans.begin() + 1 + n};
}
};