LeetCode 第 55 场双周赛 / 第 247 场周赛
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 第 55 场双周赛 / 第 247 场周赛相关的知识,希望对你有一定的参考价值。
第 55 场双周赛
1909. 删除一个元素使数组严格递增
题目描述
给你一个下标从 0 开始的整数数组 nums ,如果 恰好 删除 一个 元素后,数组 严格递增 ,那么请你返回 true ,否则返回 false 。如果数组本身已经是严格递增的,请你也返回 true 。
数组 nums 是 严格递增 的定义为:对于任意下标的 1 <= i < nums.length 都满足 nums[i - 1] < nums[i] 。
示例 1:
输入:nums = [1,2,10,5,7]
输出:true
解释:从 nums 中删除下标 2 处的 10 ,得到 [1,2,5,7] 。
[1,2,5,7] 是严格递增的,所以返回 true 。
示例 2:
输入:nums = [2,3,1,2]
输出:false
解释:
[3,1,2] 是删除下标 0 处元素后得到的结果。
[2,1,2] 是删除下标 1 处元素后得到的结果。
[2,3,2] 是删除下标 2 处元素后得到的结果。
[2,3,1] 是删除下标 3 处元素后得到的结果。
没有任何结果数组是严格递增的,所以返回 false 。
示例 3:
输入:nums = [1,1,1]
输出:false
解释:删除任意元素后的结果都是 [1,1] 。
[1,1] 不是严格递增的,所以返回 false 。
示例 4:
输入:nums = [1,2,3]
输出:true
解释:[1,2,3] 已经是严格递增的,所以返回 true 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-one-element-to-make-the-array-strictly-increasing
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
我当时的思路就是真的将一个数删除,然后判断是否是严格递增的
因为我当时想的,如果要在一个数组中跳过一个数来判断是否严格递增有点麻烦,所以就直接暴力了
现在想来,如果找到一组不满足条件的数对,分别删除一次判断就好了,不用每一个数都删除哈哈
class Solution {
public boolean canBeIncreasing(int[] nums) {
int l = nums.length;
if(check(nums))
return true;
for(int i = 0; i < l; i++){
int[] temp = new int[l - 1];
for(int j = 0; j < l - 1; j++){
if(j < i)
temp[j] = nums[j];
if(j >= i)
temp[j] = nums[j + 1];
}
if(check(temp))
return true;
}
return false;
}
public boolean check(int[] nums){
int l = nums.length;
for(int i = 0; i < l - 1; i++){
if(nums[i] >= nums[i + 1])
return false;
}
return true;
}
}
学习一下官解中如何删除一个数,判断数组是否递增的方法
class Solution {
public boolean canBeIncreasing(int[] nums) {
int l = nums.length;
for(int i = 0; i < l - 1; i++){
if(nums[i] >= nums[i + 1])
return check(nums, i) || check(nums, i + 1);
}
return true;
}
//跳过index检查是否严格递增,学习一下官解给的方法
public boolean check(int[] nums, int index){
for(int i = 0; i < nums.length - 2; i++){
int pre = i;
//如果pre下标大于等于index,那么就加一,相当于跳过index
if(pre >= index)
pre += 1;
int curr = i + 1;
//如果curr下标大于等于index,那么就加一,相当于跳过index
if(curr >= index)
curr += 1;
if(nums[pre] >= nums[curr])
return false;
}
return true;
}
}
1910. 删除一个字符串中所有出现的给定子字符串
题目描述
给你两个字符串 s 和 part ,请你对 s 反复执行以下操作直到 所有 子字符串 part 都被删除:
找到 s 中 最左边 的子字符串 part ,并将它从 s 中删除。
请你返回从 s 中删除所有 part 子字符串以后得到的剩余字符串。
一个 子字符串 是一个字符串中连续的字符序列。
示例 1:
输入:s = "daabcbaabcbc", part = "abc"
输出:"dab"
解释:以下操作按顺序执行:
- s = "daabcbaabcbc" ,删除下标从 2 开始的 "abc" ,得到 s = "dabaabcbc" 。
- s = "dabaabcbc" ,删除下标从 4 开始的 "abc" ,得到 s = "dababc" 。
- s = "dababc" ,删除下标从 3 开始的 "abc" ,得到 s = "dab" 。
此时 s 中不再含有子字符串 "abc" 。
示例 2:
输入:s = "axxxxyyyyb", part = "xy"
输出:"ab"
解释:以下操作按顺序执行:
- s = "axxxxyyyyb" ,删除下标从 4 开始的 "xy" ,得到 s = "axxxyyyb" 。
- s = "axxxyyyb" ,删除下标从 3 开始的 "xy" ,得到 s = "axxyyb" 。
- s = "axxyyb" ,删除下标从 2 开始的 "xy" ,得到 s = "axyb" 。
- s = "axyb" ,删除下标从 1 开始的 "xy" ,得到 s = "ab" 。
此时 s 中不再含有子字符串 "xy" 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-all-occurrences-of-a-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这个我还是模拟做的,在字符串匹配时,也是暴力法写的,没有用其他方法,因为觉得是第二题,应该没那么麻烦
class Solution {
int l;
int n;
public String removeOccurrences(String s, String part) {
l = s.length();
n = part.length();
int index = helper(s, part);
while(index != -1){
s = s.substring(0, index) + s.substring(index + n, l);
l -= n;
index = helper(s, part);
}
return s;
}
public int helper(String s, String part){
if(l < n)
return -1;
int i = 0;
int j = 0;
while(i <= l - n){
if(s.charAt(i) != part.charAt(j)){
i++;
}
else{
while(i < l && s.charAt(i) == part.charAt(j)){
i++;
j++;
if(j == n)
return i - n;
}
i = i - j + 1;
j = 0;
}
}
return -1;
}
}
调用自带的方法
class Solution {
public String removeOccurrences(String s, String part) {
while (s.contains(part)) {
int i = s.indexOf(part);
s = s.substring(0, i) + s.substring(i + part.length());
}
return s;
}
}
复习一下KMP算法的写法,主要是next数组的获取
class Solution {
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
if (m == 0) {
return 0;
}
//预处理next数组
int[] next = new int[m];
for (int i = 1, j = 0; i < m; i++) {
//如果匹配不成功,就在之前匹配成功的部分找是否有相同的前后缀
while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
j = next[j - 1];
}
//如果相同,就继续向后匹配
if (needle.charAt(i) == needle.charAt(j)) {
j++;
}
next[i] = j;
}
//原字符串和模式串匹配过程
for (int i = 0, j = 0; i < n; i++) {
//如果匹配不成功,找之前匹配成功的部分是否有相同的前后缀,然后从相同前缀部分继续匹配
while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
j = next[j - 1];
}
//如果匹配成功,继续向后匹配
if (haystack.charAt(i) == needle.charAt(j)) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}
}
1911. 最大子序列交替和
题目描述
一个下标从 0 开始的数组的 交替和 定义为 偶数 下标处元素之 和 减去 奇数 下标处元素之 和 。
比方说,数组 [4,2,5,3] 的交替和为 (4 + 5) - (2 + 3) = 4 。
给你一个数组 nums ,请你返回 nums 中任意子序列的 最大交替和 (子序列的下标 重新 从 0 开始编号)。
一个数组的 子序列 是从原数组中删除一些元素后(也可能一个也不删除)剩余元素不改变顺序组成的数组。比方说,[2,7,4] 是 [4,2,3,7,2,1,4] 的一个子序列(加粗元素),但是 [2,4,2] 不是。
示例 1:
输入:nums = [4,2,5,3]
输出:7
解释:最优子序列为 [4,2,5] ,交替和为 (4 + 5) - 2 = 7 。
示例 2:
输入:nums = [5,6,7,8]
输出:8
解释:最优子序列为 [8] ,交替和为 8 。
示例 3:
输入:nums = [6,2,1,2,4,5]
输出:10
解释:最优子序列为 [6,1,5] ,交替和为 (6 + 5) - 1 = 10 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-alternating-subsequence-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这个题读了以后,就想到怎么才能使这个交替和最大呢,就是找左边元素大于右边元素的数对,然后将这些数组的两个数之差加起来,当然,最后一个数对要特殊处理,因为这时,不减去最后一个数交替和是更大的,所以在最后结果加上最后减去的数就行了
那么如何使一个数对最大呢,就是在一个数的右边,找比它小的最小的数
报了一次错,数据超int了。。下次再仔细点
class Solution {
public long maxAlternatingSum(int[] nums) {
//就是说两个一组,左边元素大于右边元素就保留
//感觉像是单调栈,从左往右遍历,如果小于当前数
//就是找一个数,右边比它小的最小数,如果后面没有符合要求的数对的话
int l = nums.length;
int i = 0;
long res = 0;
while(i < l - 1){
//如果左边数大于右边数,那么说明可以有一个这样的数对
if(nums[i] > nums[i + 1]){
long temp = nums[i];
long min = nums[i + 1];
//要查找最小数的范围是从i + 2开始
i = i + 2;
while(i < l - 1){
//如果发现有左边数大于右边数的数对了,并且要比min大,就跳出循环
//因为如果比min小的话,单独提取出来并不会使交替和更大
//例如,单调递减的序列,8 6 4 2
if(nums[i] > nums[i + 1] && nums[i] > min)
break;
min = Math.min(min, nums[i]);
i++;
}
//因为取不到最后一个值,所以需要对最后一个值进行处理
if(i == l - 1)
min = Math.min(min, nums[i]);
res += temp - min;
//如果没有左边大于右边的数对,那么就一直往后判断
}else{
i++;
}
}
//最后加上最后一个值
res += nums[l - 1];
return res;
}
}
看了大佬们的思路,感觉自己是个fw
首先是贪心,这个题就相当于在最高的时候买入股票,在最低的时候卖出,然后可以买卖无数次
class Solution {
public long maxAlternatingSum(int[] nums) {
//贪心
int n = nums.length;
long res = 0;
int pre = nums[0];
for(int i = 1; i < n; i++){
//如果比之前买入价格小,就卖出,然后再把当前股票买入
if(nums[i] < pre){
res += pre - nums[i];
pre = nums[i];
//如果大于之前买入的价格,就换成新的价格
}else{
pre = nums[i];
}
}
//最后相当于有一个0,需要将最后买入的股票卖出
return res + pre;
}
}
动规:
odd[i] 表示在数组nums 的前缀nums[0…i] 中选择元素组成子序列,且最后一个选择的元素的下标是奇数时,可以得到的最大交替和。
even[i] 表示在nums[0…i] 中选择元素组成子序列,且最后一个选择的元素的下标是偶数时,可以得到的最大交替和。
对于odd而言,如果选择nums[i],因为最后一个元素下标是奇数,所以odd[i] = max{odd[i - 1], even[i - 1] - nums[i]};
对于even而言,如果选择nums[i],因为最后一个元素下标是偶数,所以even[i] = max{even[i - 1], even[i - 1] + nums[i]};
最后结果最后一个元素下标肯定是偶数,所以结果就是even[l - 1]
class Solution {
public long maxAlternatingSum(int[] nums) {
//动规
int l = nums.length;
long[] odd = new long[l];
long[] even = new long[l];
even[0] = nums[0];
for(int i = 1; i < l; i++){
odd[i] = Math.max(odd[i - 1], even[i - 1] - nums[i]);
even[i] = Math.max(even[i - 1], odd[i - 1] + nums[i]);
}
return even[l - 1];
}
}
1912. 设计电影租借系统
题目描述
你有一个电影租借公司和 n 个电影商店。你想要实现一个电影租借系统,它支持查询、预订和返还电影的操作。同时系统还能生成一份当前被借出电影的报告。
所有电影用二维整数数组 entries 表示,其中 entries[i] = [shopi, moviei, pricei] 表示商店 shopi 有一份电影 moviei 的拷贝,租借价格为 pricei 。每个商店有 至多一份 编号为 moviei 的电影拷贝。
系统需要支持以下操作:
Search:找到拥有指定电影且 未借出 的商店中 最便宜的 5 个 。商店需要按照 价格 升序排序,如果价格相同,则 shopi 较小 的商店排在前面。如果查询结果少于 5 个商店,则将它们全部返回。如果查询结果没有任何商店,则返回空列表。
Rent:从指定商店借出指定电影,题目保证指定电影在指定商店 未借出 。
Drop:在指定商店返还 之前已借出 的指定电影。
Report:返回 最便宜的 5 部已借出电影 (可能有重复的电影 ID),将结果用二维列表 res 返回,其中 res[j] = [shopj, moviej] 表示第 j 便宜的已借出电影是从商店 shopj 借出的电影 moviej 。res 中的电影需要按 价格 升序排序;如果价格相同,则 shopj 较小 的排在前面;如果仍然相同,则 moviej 较小 的排在前面。如果当前借出的电影小于 5 部,则将它们全部返回。如果当前没有借出电影,则返回一个空的列表。
请你实现 MovieRentingSystem 类:
MovieRentingSystem(int n, int[][] entries) 将 MovieRentingSystem 对象用 n 个商店和 entries 表示的电影列表初始化。
List<Integer> search(int movie) 如上所述,返回 未借出 指定 movie 的商店列表。
void rent(int shop, int movie) 从指定商店 shop 借出指定电影 movie 。
void drop(int shop, int movie) 在指定商店 shop 返还之前借出的电影 movie 。
List<List<Integer>> report() 如上所述,返回最便宜的 已借出 电影列表。
注意:测试数据保证 rent 操作中指定商店拥有 未借出 的指定电影,且 drop 操作指定的商店 之前已借出 指定电影。
示例 1:
输入:
["MovieRentingSystem", "search", "rent", "rent", "report", "drop", "search"]
[[3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]], [1], [0, 1], [1, 2], [], [1, 2], [2]]
输出:
[null, [1, 0, 2], null, null, [[0, 1], [1, 2]], null, [0, 1]]
解释:
MovieRentingSystem movieRentingSystem = new MovieRentingSystem(3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]);
movieRentingSystem.search(1); // 返回 [1, 0, 2] ,商店 1,0 和 2 有未借出的 ID 为 1 的电影。商店 1 最便宜,商店 0 和 2 价格相同,所以按商店编号排序。
movieRentingSystem.rent(0, 1); // 从商店 0 借出电影 1 。现在商店 0 未借出电影编号为 [2,3] 。
movieRentingSystem.rent(1, 2); // 从商店 1 借出电影 2 。现在商店 1 未借出的电影编号为 [1] 。
movieRentingSystem.report(); // 返回 [[0, 1], [1, 2]] 。商店 0 借出的电影 1 最便宜,然后是商店 1 借出的电影 2 。
movieRentingSystem.drop(1, 2); // 在商店 1 返还电影 2 。现在商店 1 未借出的电影编号为 [1,2] 。
movieRentingSystem.search(2); // 返回 [0, 1] 。商店 0 和 1 有未借出的 ID 为 2 的电影。商店 0 最便宜,然后是商店 1 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/design-movie-rental-system
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
思路可能不太难,就是写起来麻烦,细节比较多,我这里直接借鉴了,加了点注释
class MovieRentingSystem {
//键为电影,值为以价格和商店排序的优先队列,方便租借
Map<Integer, PriorityQueue<int[]>> movies = new HashMap<>();
//表示借出去的电影元素
Set<int[]> set = new HashSet<>();
//借出去的电影
PriorityQueue<int[]> jie = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
//首先按价格排序
if(o1[2] != o2[2])
return o1[2]-o2[2];
//再按商店排序
else if(o1[0] != o2[0])
return o1[0]-o2[0];
//再按movie排序
else
return o1[1]-o2[1];
}
});
//存储每个商店的电影
Map<Integer, int[]>[] shops;
public MovieRentingSystem(int n, int[][] entries) {
//有n个商店,就有n个Map
shops = new Map[n];
for(int i = 0; i < n; i++)
shops[i]= new HashMap();
//将电影加入商店哈希表中
for(int i = 0; i < entries.length; i++){
//当前电影元素
int[] tem = entries[i];
//如果当前电影没有存储到优先队列中,那么创建当前电影的优先队列
if(movies.get(tem[以上是关于LeetCode 第 55 场双周赛 / 第 247 场周赛的主要内容,如果未能解决你的问题,请参考以下文章