错了两发,我好恨呐~
5930. 两栋颜色不同且距离最远的房子
思路一:枚举
时间复杂度:
空间复杂度:
使用嵌套的两个 for 循环,枚举任意一对房子 。记录不同颜色的 的最大差值即可。
一个小优化是,可根据已知的最大差值,加速内层循环,详见注释。
class Solution {
public:
int maxDistance(vector<int>& c) {
// anw 用以存储答案。
int anw = 0;
for (int i = 0; i < c.size(); i++) {
// [i+1,i+anw] 区间内的房子不可能比 anw 更远啦
// 因此直接从 i+anw+1 开始枚举咯
for (int j = i+anw+1; j < c.size(); j++) {
if (c[i] != c[j]) {
anw = j-i;
}
}
}
return anw;
}
};
5201. 给植物浇水
思路一:模拟
时间复杂度:
空间复杂度:
假设在给第 棵植物浇水前,位置为 ,剩余水量为 ,已走步数为 。根据题意,此时有两种情形:
-
。此时只能从 走至 ,然后倒出 单位的水。各变量更新如下:
-
。此时只能从 走回 ,灌满水后再走至 ,然后倒出 单位的水。各变量更新如下:
另外,根据题意,不难得出初始时的各变量值:
于是,我们可通过一层 for 计算出答案。
class Solution {
public:
int wateringPlants(vector<int>& plants, int capacity) {
int sum = 0;
int pos = -1;
int water = capacity;
for (int i = 0; i < plants.size(); i++) {
if (water >= plants[i]) {
sum += i - pos;
pos = i;
water -= plants[i];
} else {
sum += pos - (-1) + i - (-1);
pos = i;
water = capacity - plants[i];
}
}
return sum;
}
};
5186. 区间内查询数字的频率
思路一:分块统计
时间复杂度:
空间复杂度:
首先将 个数字,按位置均分为 个块,每块约有 个数字。
如上图所示,长度为 10 的数组,依次被分为三个块。每个块可由三个变量描述:
- L:该块的左端点
- R:该块的右端点
- 哈希表:统计该块内每种颜色的出新次数。
上述块信息,可在 时间内完成初始化。
对于每次查询,记作 表示查询区间 内 的出现次数。第 个块代表的区间 与 区间 的关系可归纳为以下三种:
- 完全不包含。即 或者 。此情形下该块不可能对答案有影响。
- 完全包含。即 。此情形下该块对答案的影响,可通过哈希表 的获得。
- 部分包含。该块中的部分数字对答案有影响,需要依次对这些数字进行检查。值得注意的是,对于每次查询,这样的块不会超过两个,换言之,每次查询需检查的数字不过超过 个。
综上所述,每次查询,最多检查 个块, 个数字,因此时间复杂度为 。
class RangeFreqQuery {
// 块信息,tuple 的三个元素依次为 L, R, dict。
vector<tuple<int, int, unordered_map<int, int>>> block;
// data: 输入数组的拷贝
vector<int> &data;
public:
RangeFreqQuery(vector<int>& arr) : data(arr) {
int n = arr.size();
// 计算块的长度上限 step。
int step = max(1, int(sqrt(n)));
// 初始化 block
for (int i = 0; i < n; i++) {
if (block.empty() || get<1>(block.back()) < i) {
// 前一个块装满了,需要新创建一个了。
block.emplace_back(i, i+step-1, unordered_map<int, int>());
}
// 更新 dict
(get<2>(block.back()))[arr[i]]++;
}
}
int query(int left, int right, int value) {
int anw = 0;
// 遍历所有块
for (int i = 0; i < block.size(); i++) {
// 去除左右端点
int L = get<0>(block[i]);
int R = get<1>(block[i]);
if (left <= L && R <= right) {
// 完全包含的情形
const auto &dict = get<2>(block[i]);
auto it = dict.find(value);
if (it != dict.end()) {
anw += it->second;
}
} else if (right < L) {
// 完全不包含的情形
break;
} else if (R < left) {
// 完全不包含的情形
continue;
} else {
// 部分包含的情形,需依次检查包含的那些数字
for (int j = max(L,left), rr = min(right, R); j <= rr; j++) {
if (data[j] == value) {
anw += 1;
}
}
}
}
return anw;
}
};
/**
* Your RangeFreqQuery object will be instantiated and called as such:
* RangeFreqQuery* obj = new RangeFreqQuery(arr);
* int param_1 = obj->query(left,right,value);
*/
思路二:二分
时间复杂度:
空间复杂度:
这个思路就很好理解啦。
在构造函数中,记录每个数字出现的位置。每个数字的位置信息构成了一个有序的一维数组。
对于每次查询 ,首先检查 是否出现过,如果没有则答案为 0。如果出现过,则在 对应的一维数组上,通过二分求得答案。
class RangeFreqQuery {
unordered_map<int, vector<int>> pos;
public:
RangeFreqQuery(vector<int>& arr) {
for (int i = 0; i < arr.size(); i++) {
pos[arr[i]].push_back(i);
}
}
int query(int left, int right, int value) {
auto it = pos.find(value);
if (it == pos.end()) {
return 0;
}
const auto &p = it->second;
return upper_bound(p.begin(), p.end(), right) - lower_bound(p.begin(), p.end(), left);
}
};
5933. k 镜像数字的和
思路一:预处理
时间复杂度:
空间复杂度:
先预处理出 个镜像数字,每种进制 个。
最大的数字为 。因为要求数字镜像,我们可仅枚举数字的前半部分,这样枚举的范围就大大缩小啦。
bool init_flag = false;
vector<int64_t> arr[10];
bool is_mirror(const std::string &p) {
int n = p.size();
for (int i = n/2; i >= 0; i--) {
if (p[i] != p[n-i-1]) {
return false;
}
}
return true;
}
void init() {
// 根据题意,最多找出 (9-2+1)*30 = 240 个镜像数字
int cnt = 0;
for (int i = 1; cnt < 240; i++) {
// 枚举数字长度 i
int part = (i+1)/2;
int64_t L = 1;
for (int i = 1; i < part; i++) {
L *= 10;
}
int64_t R = L * 10;
// 枚举前一半数字 num ∈ [L, R)
for (int num = L; num < R && cnt < 240; num++) {
auto tmp = std::to_string(num);
// 根据长度 i 以及 前一半数组 num,构造出整个数字 tmp。
if (i&1) {
for (int i = tmp.size()-2; i >= 0; i--) {
tmp += tmp[i];
}
} else {
for (int i = tmp.size()-1; i >= 0; i--) {
tmp += tmp[i];
}
}
int64_t tmp_num = std::atol(tmp.c_str());
for (int j = 2; j <= 9; j++) {
// j 进制已经有 30 个了,没必要再算了
if (arr[j].size() >= 30) {
continue;
}
std::string knum;
int64_t tmp = tmp_num;
// 转化成 k 进制数字 knum
while(tmp > 0) {
knum += ('0' + tmp%j);
tmp /= j;
}
// 检查 knum 是否镜像
if (is_mirror(knum)) {
arr[j].push_back(tmp_num);
cnt++;
}
}
}
}
}
class Solution {
public:
long long kMirror(int k, int n) {
if (!init_flag) {
init_flag = true;
// 开始打表
init();
}
// 统计答案
int64_t sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[k][i];
}
return sum;
}
};