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 场周赛的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode第82场双周赛

LeetCode第82场双周赛

LeetCode第69场双周赛

LeetCode 第 14 场双周赛

LeetCode 第63场双周赛复盘

leetcode第 46 场双周赛