【LeetCode】第74场双周赛参赛实录

时间:2022-03-19 22:30-24:00

地址:竞赛 - 力扣 (LeetCode)

结局:本次依然是做了三道。前三题应该说没有太难,不过有一些边界条件确实有点怪,让我错了好几次。第四题虽然知道要使用动态规划,但瞪大眼珠子想了20多分钟,也没一个靠谱的思路,也就果断下播了。

【LeetCode】第74场双周赛参赛实录_第1张图片

2206. 将数组划分成相等数对

给你一个整数数组 nums ,它包含 2 * n 个整数。

你需要将 nums 划分成 n 个数对,满足:

  • 每个元素只属于一个数对。
  • 同一数对中的元素相等 。

如果可以将 nums 划分成 n 个数对,请你返回 true ,否则返回 false 。

示例 1:
输入:nums = [3,2,3,2,2,2]

输出:true

解释:
nums 中总共有 6 个元素,所以它们应该被划分成 6 / 2 = 3 个数对。
nums 可以划分成 (2, 2) ,(3, 3) 和 (2, 2) ,满足所有要求。
 

示例 2:
输入:nums = [1,2,3,4]

输出:false

解释:
无法将 nums 划分成 4 / 2 = 2 个数对且满足所有要求。

提示:
nums.length == 2 * n

1 <= n <= 500

1 <= nums[i] <= 500

思路:这题不难理解哈,排序之后判断前后两个数字是否相等就好。因为数组长度一定是2的倍数,数字取值范围也不大,因此可以简单处理。这个复杂度是 O(nlogn)。

 public boolean divideArray(int[] nums) {
        int len = nums.length;
        if(len%2!=0){
            return false;
        }
        Arrays.sort(nums);
        for(int i=0;i

后续整理:快速写完之后,还是花了点时间去想,能不能O(n)搞定。其实也不复杂,只需要确认每个数出现的次数是偶数就行了。弄一个hashmap,完全可以在一次遍历内就搞定。

 public boolean divideArray(int[] nums) {
         HashMap map = new HashMap<>();
        int odd = 0;
        for (int num : nums) {
            int res = map.getOrDefault(num, 0) + 1;
            map.put(num, res);
            if (res % 2 == 0) {
                odd--;
            } else {
                odd++;
            }
        }
        return odd == 0;
    }

2207. 字符串中最多数目的子字符串

给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern ,两者都只包含小写英文字母。

你可以在 text 中任意位置插入 一个 字符,这个插入的字符必须是 pattern[0] 或者 pattern[1] 。注意,这个字符可以插入在 text 开头或者结尾的位置。

请你返回插入一个字符后,text 中最多包含多少个等于 pattern 的 子序列 。

子序列 指的是将一个字符串删除若干个字符后(也可以不删除),剩余字符保持原本顺序得到的字符串。

示例 1:

输入:text = "abdcdbc", pattern = "ac"
输出:4
解释:
如果我们在 text[1] 和 text[2] 之间添加 pattern[0] = 'a' ,那么我们得到 "abadcdbc" 。那么 "ac" 作为子序列出现 4 次。
其他得到 4 个 "ac" 子序列的方案还有 "aabdcdbc" 和 "abdacdbc" 。
但是,"abdcadbc" ,"abdccdbc" 和 "abdcdbcc" 这些字符串虽然是可行的插入方案,但是只出现了 3 次 "ac" 子序列,所以不是最优解。
可以证明插入一个字符后,无法得到超过 4 个 "ac" 子序列。


示例 2:

输入:text = "aabb", pattern = "ab"
输出:6
解释:
可以得到 6 个 "ab" 子序列的部分方案为 "aaabb" ,"aaabb" 和 "aabbb" 。
 

提示:

1 <= text.length <= 10^{5}
pattern.length == 2
text 和 pattern 都只包含小写英文字母。

思路:这题当时思路还是来的很快的。以pattern为ab为例,最大的数量一定是把a放在text最左边,或者把b放在text最右边。当时我把计算拆为两步,(1)计算a\b出现次数ca\cb,以及原序列中ab子序列的次数--这个可以通过遇到b时,左边有多少a来进行一个叠加;(2)原次数+max(ca,cb)。虽然想法不错,写的也不慢,不过中间错了两次,是在遗憾。

public long maximumSubsequenceCount(String text, String pattern) {
        //错误二,不用long,统计数据会越界,毕竟长度已经有了10^5
        long res = 0; 
        char p1 = pattern.charAt(0);
        char p2 = pattern.charAt(1);
        long c1 = 0;
        long c2 = 0;
        for(char c:text.toCharArray()){
            if(c==p1){
               c1++;
            }
            else if(c==p2){
                c2++;
                res+=c1;
            }
        }
        //错误一,没有考虑pattern中两个字符一致的情况
        if(p1==p2){
            res = c1 + (c1-1)*c1/2;
            return res;
        }else{
             res+=Math.max(c1,c2);
            return res;
        }
    }

后续整理:思路倒是没啥,看看别人的代码是多么简洁。

  //https://leetcode-cn.com/u/arignote/
  public long maximumSubsequenceCount(String text, String pattern) {
        long count = 0, count1 = 0, count2 = 0;
        for (char c : text.toCharArray()) {
            count += c == pattern.charAt(1) ? count1 : 0;
            count2 += c == pattern.charAt(1) ? 1 : 0;
            count1 += c == pattern.charAt(0) ? 1 : 0;
        }
        return count + Math.max(count1, count2);
    }

2208. 将数组和减半的最少操作次数

给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)

请你返回将 nums 数组和 至少 减少一半的 最少 操作数。

示例 1:

输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。

示例 2:

输入:nums = [3,8,20]
输出:3
解释:初始 nums 的和为 3 + 8 + 20 = 31 。
以下是将数组和减少至少一半的一种方法:
选择数字 20 并减小为 10 。
选择数字 10 并减小为 5 。
选择数字 3 并减小为 1.5 。
最终数组为 [1.5, 8, 5] ,和为 1.5 + 8 + 5 = 14.5 。
nums 的和减小了 31 - 14.5 = 16.5 ,减小的部分超过了初始数组和的一半, 16.5 >= 31/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。

提示:

1 <= nums.length <= 10^{5}
1 <= nums[i] <= 10^{7}

思路:这个的思路也不复杂,每次都缩小最大的数,再去看看有没有减少到一半,把次数记下来就行了。那每次缩小后,当前的最大值怎么找呢?为了图快,我使用了反复排序大法来拿最大值,成功超时。后来还是老实使用了PriorityQueue来做(实力不足,就没有自己实现堆了)。

 public int halveArray(int[] nums) {
        int count = 0;
        double sum = 0;
        int len = nums.length;
        PriorityQueue priorityQueue = new PriorityQueue<>(new Comparator() {
            @Override
            public int compare(Double o1, Double o2) {
                return o2-o1>0?1:-1;
            }
        });
        for(int i=0;i

后续整理:主流思路。没有什么特别值得说的。

2209. 用地毯覆盖后的最少白色砖块

给你一个下标从 0 开始的 二进制 字符串 floor ,它表示地板上砖块的颜色。

floor[i] = '0' 表示地板上第 i 块砖块的颜色是 黑色 。
floor[i] = '1' 表示地板上第 i 块砖块的颜色是 白色 。
同时给你 numCarpets 和 carpetLen 。你有 numCarpets 条 黑色 的地毯,每一条 黑色 的地毯长度都为 carpetLen 块砖块。请你使用这些地毯去覆盖砖块,使得未被覆盖的剩余 白色 砖块的数目 最小 。地毯相互之间可以覆盖。

请你返回没被覆盖的白色砖块的 最少 数目。

示例 1:

【LeetCode】第74场双周赛参赛实录_第2张图片


示例 2:

【LeetCode】第74场双周赛参赛实录_第3张图片

提示:

1 <= carpetLen <= floor.length <= 1000
floor[i] 要么是 '0' ,要么是 '1' 。
1 <= numCarpets <= 1000

思路:这题直观上能感觉到是一个动态规划的题目,但转移关系是着实摸不着头脑。挣扎了二十多分钟后放弃了。

后续整理

dp[i][j] 表示考虑前面长度为 j 的地板中,用了 i 条地毯,最后剩余最少白色砖块。

状态转移方程:

  1. 当 i == 0 时,此时不用地毯, dp[0][j] 表示前 j 个砖块中白色砖块数目。
  2. 当 i != 0 时,dp[i][j] = min(dp[i][j - 1] + count[j], dp[i - 1][j - carpetLen]);

其中 count[j] 表示第 j 块地板是否为白色砖块,

  • dp[i][j-1] + count[j] 表示前 j - 1 块地板用了i 条地毯,剩余最少的白色砖块,加上第 j 块地板是否为白色砖块。(即第 j 个位置不用地毯)
  • dp[i - 1][j - carpetLen] 表示在下标 j 这里用了地毯,那么 dp[i][j] 就和前 (j - carpetLen) 块地板中用了 i - 1 条地毯剩余的白色砖块数目一致。(即第 j 个位置用了地毯)

链接:https://leetcode-cn.com/problems/minimum-white-tiles-after-covering-with-carpets/solution/tan-xin-by-cyber-space-iwvd/

class Solution {
    public int minimumWhiteTiles(String floor, int n, int carpetLen) {
        var m = floor.length();
        var f = new int[n + 1][m];
        f[0][0] = floor.charAt(0) % 2;
        for (var i = 1; i < m; ++i)
            f[0][i] = f[0][i - 1] + floor.charAt(i) % 2;
        for (var i = 1; i <= n; ++i)
            // j < carpetLen 的 f[i][j] 均为 0
            for (var j = carpetLen; j < m; ++j)
                f[i][j] = Math.min(f[i][j - 1] + floor.charAt(j) % 2, f[i - 1][j - carpetLen]);
        return f[n][m - 1];
    }
}
//链接:https://leetcode-cn.com/problems/minimum-white-tiles-after-covering-with-carpets/solution/by-endlesscheng-pa3v/

你可能感兴趣的:(2.数据结构与算法,leetcode,java)