又进前一百了,真带劲儿呀。主要是这次题目比较简单,都是些手速题,所以我才有机会~
5952. 环和杆
思路:模拟,哈希
时间复杂度:
空间复杂度:
遍历字符串,统计每个杆上出现的颜色即可。借助 std::unordered_map 和 std::unordered_set,代码会很简便。
class Solution {
public:
int countPoints(string rings) {
// cnt 的 first 表示杆
// cnt 的 second 记录杆上的出现的颜色
unordered_map<char, unordered_set<char>> cnt;
// anw 记录答案
int anw = 0;
// 开始遍历
for (int i = 0; i < rings.size(); i += 2) {
char c = rings[i];
char p = rings[i+1];
// 在杆p上插入颜色 c,如果插入成功且颜色数为 3,则说明新找到了一个集齐颜色的杆
if(cnt[p].insert(c).second && cnt[p].size() == 3) {
anw++;
}
}
return anw;
}
};
5953. 子数组范围和
方法一
思路:暴力枚举
时间复杂度:
空间复杂度:
因为题目的数据范围较小,可以用两层循环枚举所有子数组。
class Solution {
public:
long long subArrayRanges(vector<int>& nums) {
// anw 保存答案
int64_t anw = 0;
int n = nums.size();
// 两层循环
for (int i = 0; i < n; i++) {
// l 表示区间 [i,j] 内的最小值
// r 表示区间 [i,j] 内的最大值
int l = nums[i], r = nums[i];
for (int j = i; j < n; j++) {
// 更新 l, r
l = min(l, nums[j]);
r = max(r, nums[j]);
// 更新答案
anw += (r - l);
}
}
return anw;
}
};
方法二
思路:单调栈
时间复杂度:
空间复杂度:
假设我们知道每个元素 分别是 个子数组的最小值, 个子数组的最大值,那么答案可表示为:
我们可使用单调栈求解 和 。下面以 为例讲解一下思路。
设有栈 。我们从前先后将元素的位置 放入栈中,在放入 之前, 需满足以下条件之一:
- 为空。
- 。
否则,弹出栈顶元素直到满足要求。
在此过程中,可以得出「以 作为最小值的子数组的左边界 」—— 在 满足放入要求时:
- 为空,则左边界 为 0。
- 不为空,则左边界 为 。
同样的,我们从后向前遍历,按照上述步骤可以得到「以 作为最小值的子数组的右边界 」。需要注意的是,为了避免重复统计,限制条件应改为:
- 为空。
- ( 改为 )
换言之,如果子数组包含多个最小值时,以下标较小的为最小值。
那么 可表示为:
同理,按照类似操作,可以得出 。
class Solution {
public:
void work(const vector<int> &nums, const function<bool(int, int)> &c0, const function<bool(int, int)> &c1, vector<int> &data) {
int n = nums.size();
stack<int> st;
vector<int> L(n, 0), R(n, 0);
// 求解 L
for (int i = 0; i < n; i++) {
while (false == st.empty() && !c0(nums[st.top()], nums[i])) {
st.pop();
}
L[i] = (st.empty() ? 0 : st.top()+1);
st.push(i);
}
st = stack<int>();
// 求解 R
for (int i = n-1; i >= 0; i--) {
while (false == st.empty() && !c1(nums[st.top()], nums[i])) {
st.pop();
}
R[i] = (st.empty() ? n-1 : st.top()-1);
st.push(i);
}
// 求解 data,即传进来的 low 或 high
for (int i = 0; i < n; i++) {
data[i] = (R[i]-i+1)*(i-L[i]+1);
}
}
long long subArrayRanges(vector<int>& nums) {
int n = nums.size();
vector<int> low(n, 0), high(n, 0);
// 求解 low
work(nums,
[](int lhs, int rhs) { return lhs <= rhs;},
[](int lhs, int rhs) { return lhs < rhs;},
low);
// 求解 high
work(nums,
[](int lhs, int rhs) { return lhs >= rhs;},
[](int lhs, int rhs) { return lhs > rhs;},
high);
// 计算答案
int64_t anw = 0;
for (int i = 0; i < n; i++) {
anw += 1L * nums[i] * (high[i] - low[i]);
}
return anw;
}
};
2105. 给植物浇水 II
思路:模拟
时间复杂度:
空间复杂度:
比较直白的模拟题啦。Alice 浇前 棵植物,Bob 浇后 棵植物。如果 是奇数,那么根据 Alice 和 Bob 的剩余水量决定是谁来浇。
在模拟过程中,记录灌水的次数即可。详见注释。
class Solution {
public:
int minimumRefill(vector<int>& plants, int capacityA, int capacityB) {
int n = plants.size();
// anw 用来保存答案。
int anw = 0;
// remainA 记录 Alice 浇完 plants[i] 之后的剩余量
int remainA = capacityA;
for (int i = 0; i < n/2; i++) {
if (remainA >= plants[i]) {
// 如果水足够,那么直接浇
remainA -= plants[i];
} else {
// 否则先灌满水,再浇。
remainA = capacityA - plants[i];
// 记录一次灌水次数
anw++;
}
}
// remainB 类似
int remainB = capacityB;
for (int i = 0; i < n/2; i++) {
if (remainB >= plants[n-1-i]) {
remainB -= plants[n-1-i];
} else {
remainB = capacityB - plants[n-1-i];
anw++;
}
}
// 如果是奇数,根据剩余水量决定由谁来浇。
if (n&1) {
int r = remainA;
if (remainA < remainB) {
r = remainB;
}
if (r < plants[n/2]) {
anw++;
}
}
return anw;
}
};
2106. 摘水果
思路:模拟,前缀和
时间复杂度:
空间复杂度:
有四种策略来分配 :
- 向左走 步,覆盖区间
- 向右走 步,覆盖区间
- 先向左走 步,再向右走 步,覆盖区间
- 先想右走 步,再向左走 步,覆盖区间
其中,。
那么问题变为了,如何求解给定区间内的水果水量,显然可以借助前缀和咯。
具体编程实现时,需要注意边界溢出的情形。详见注释。
class Solution {
public:
int maxTotalFruits(vector<vector<int>>& fruits, int startPos, int k) {
// 为了便于编写代码,我们将所有的位置都 + 1。
const int MAXN = 200001;
int64_t pos[MAXN+1] = {0};
// 先统计每个位置上的水果数量。
for (const auto &f : fruits) {
pos[f[0]+1] = f[1];
}
// 预处理前缀和。
for (int i = 1; i <= MAXN; i++) {
pos[i] += pos[i-1];
}
int s = startPos + 1;
// 仅向右走
int sol1 = pos[min(s+k, MAXN)] - pos[s-1];
// 仅向左走
int sol2 = pos[s] - pos[max(s-k-1, 0)];
// 先左后右
int sol3 = 0;
// 枚举先向左走多少步
for (int i = 1; i <= k/2; i++) {
int tmp = pos[min(MAXN,s+(k-2*i))] - pos[max(s-i-1, 0)];
sol3 = max(tmp, sol3);
}
// 先右后左
int sol4 = 0;
// 枚举先向右走多少步
for (int i = 1; i <= k/2; i++) {
int tmp = pos[min(MAXN, s + i)] - pos[max(0, s - (k-2*i) -1)];
sol4 = max(tmp, sol4);
}
return max(max(sol1, sol2), max(sol3, sol4));
}
};