LeetCode 第 55 场双周赛 题解及思路
赛题传送门
1909. 删除一个元素使数组严格递增
赛题
我们可以遍历数组 nums
中求出第一个满足条件 nums[idx] <= nums[idx - 1]
的下标 idx
,这就意味着下标为 idx
或 idx - 1
的元素必须删除一个才 可能 满足条件。
bool canBeIncreasing(vector<int>& nums) {
int idx = -1;
for (int i = 1; i < nums.size(); i++)
if (nums[i] <= nums[i - 1])
if (idx != -1) return false;
else idx = i;
}
如果没有找到这样的下标则返回 true
。如果找到了两个及以上这样的下标则返回 false
。否则我们可以删掉下标 idx
的元素且判断 idx - 1
和 idx + 1
的元素是否满足递增,或者我们可以删掉下标 idx - 1
的元素且判断 idx - 2
和 idx
的元素是否满足递增。
返回值注意边界处理。
return idx == -1 || idx == 1 || idx == nums.size() - 1 || nums[idx + 1] > nums[idx - 1] || nums[idx] > nums[idx - 2];
完整代码见 GitHub。
1910. 删除一个字符串中所有出现的给定子字符串
赛题
想都没想,直接模拟就好了,不断使用 C++
中的 find
函数对 s
查找子串 part
,如果能找到则从 s
删除这个子串并重复上述过程,如果找不到就返回 s
作为答案。
string removeOccurrences(string s, string part) {
int len = part.length();
while (true) {
int start_idx = s.find(part);
if (start_idx == string::npos) break;
else s = s.substr(0, start_idx) + s.substr(start_idx + len);
};
return s;
}
完整代码见 GitHub。
1911. 最大子序列交替和
看上去就像是合格的 动态规划 题,最简单的即按照 数组长度 来拆分,即我们考虑 nums
的前缀子数组 nums[0] - nums[i - 1]
,假设我们已经在前缀子数组中选取了 奇数 个元素用作最大交替和的子序列,则如果选取当前元素就是 奇数 下标(注意下标从 0
开始)是要被减去的。
因此我们分奇偶来分别动态规划即可, d p [ i ] [ 0 ] dp[i][0] dp[i][0] 代表在前 i i i 个元素中选取偶数个元素的最大子序列交替和, d p [ i ] [ 1 ] dp[i][1] dp[i][1] 则代表在前 i i i 个元素中选取奇数个元素的最大子序列交替和。
d p [ i ] [ 0 ] = { m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − n u m s [ i ] ) i ≥ 1 0 i = 0 dp[i][0] = \begin{cases} max(dp[i - 1][0], dp[i - 1][1] - nums[i]) & i \geq 1 \\ 0 & i = 0 \\ \end{cases} dp[i][0]={max(dp[i−1][0],dp[i−1][1]−nums[i])0i≥1i=0
d p [ i ] [ 1 ] = { m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + n u m s [ i ] ) i ≥ 1 n u m s [ i ] i = 0 dp[i][1] = \begin{cases} max(dp[i - 1][1], dp[i - 1][0] + nums[i]) & i \geq 1 \\ nums[i] & i = 0 \\ \end{cases} dp[i][1]={max(dp[i−1][1],dp[i−1][0]+nums[i])nums[i]i≥1i=0
完整代码见 GitHub。
1912. 设计电影租借系统
太复杂了题目,先好好读题 3
遍发现大致就是要实现 有优先级的查询 因此考虑使用 map
,我们定义 node
结构体来存储每个 entry
并且定义优先级。
struct node {
int shop;
int movie;
int price;
node(int shop, int movie, int price): shop(shop), movie(movie), price(price) {};
bool operator<(const node& a) const {
if (this->price != a.price) return this->price < a.price;
else if (this->shop != a.shop) return this->shop < a.shop;
else return this->movie < a.movie;
};
};
然后我们发现 shop
和 movie
的上限是 3 * 10^5
,那么我们就暴力地定义一个这么大的数组,数组中每个元素都是对应 shop(movie)
的 movie(shop)
的 map
用来快速查询。
vector< map<node, int, less<node> > > movie2shop; // 通过 movie 查询 shop
vector< map<node, int, less<node> > > shop2movie; // 通过 shop 查询 movie
map<int, map<int, int>> movieshop2price; // 通过 movie 和 shop 查询 price
map<node, node, less<node> > rentmovies; // 已经解出的 movie
在 初始化 过程中,我们遍历每个 entry
来插入各个 map
。
for (const vector<int>& entry : entries) {
int shop = entry[0], movie = entry[1], price = entry[2];
movie2shop[movie].insert(pair<node, int>(node(shop, movie, price), shop));
shop2movie[shop].insert(pair<node, int>(node(shop, movie, price), movie));
movieshop2price[movie][shop] = price;
};
search
的话则直接根据 movie2shop
去寻找至多 5 个条目返回即可。
vector<int> search(int movie) {
map<node, int, less<node> > m = movie2shop[movie];
vector<int> answer;
for (map<node, int, less<node> >::iterator iter = m.begin(); iter != m.end() && answer.size() < 5; ++iter)
answer.push_back(iter->second);
return answer;
}
rent
的话则需要将 shop2movie
和 movie2shop
中对应的 entry
删去,并且在 rentmovies
中插入方便 report
快速查找。
void rent(int shop, int movie) {
node n = node(shop, movie, movieshop2price[movie][shop]);
shop2movie[shop].erase(shop2movie[shop].find(n));
movie2shop[movie].erase(movie2shop[movie].find(n));
rentmovies.insert(pair<node, node>(n, n));
return;
}
drop
的话则需要在 shop2movie
和 movie2shop
分别增加对应的 entry
,并且在 rentmovies
中删去对应的 entry
以保持数据的一致性。
void drop(int shop, int movie) {
node n = node(shop, movie, movieshop2price[movie][shop]);
shop2movie[shop].insert(pair<node, int>(n, movie));
movie2shop[movie].insert(pair<node, int>(n, shop));
rentmovies.erase(rentmovies.find(n));
return;
}
report
则直接从 rentmovies
中选取至多 5 个 entry
返回即可。
vector<vector<int> > report() {
vector<vector<int> > answer;
for (map<node, node, less<node> >::iterator iter = rentmovies.begin(); iter != rentmovies.end() && answer.size() < 5; ++iter) {
node n = iter->first;
answer.push_back({n.shop, n.movie});
};
return answer;
}
完整代码见 GitHub。
这场双周赛是我在比赛结束后才打的虚拟竞赛,最近虾皮挺火的就想着做做他冠名的周赛,没想到还是挺简单的,最后一道题也是更偏向于设计(而不是算法)以及思路的严谨性。
好啦,以上就是力扣第 55 场双周赛的全部思路啦。最后,欢迎关注我的 GitHub 账号。