LeetCode第245场周赛(6.13)总结

前言

只写前三题,第四题实力不够暂时不写

第一题 1897. 重新分配字符使所有字符串都相等(简单)

给你一个字符串数组 words(下标 从 0 开始 计数)。
在一步操作中,需先选出两个 不同 下标 i 和 j,其中 words[i] 是一个非空字符串,接着将 words[i] 中的 任一 字符移动到 words[j] 中的 任一 位置上。
如果执行任意步操作可以使 words 中的每个字符串都相等,返回 true ;否则,返回 false 。
提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] 由小写英文字母组成

当时思路

先分析,只需要得出能否达成目标,不需要取出最小操作步骤之类的,很显然只需要每个字母能均匀的分散到各个words就可以。
再看问题规模,长度在一百以内,每个词长度也在一百以内,遍历一遍操作次数在10000以内,可以直接遍历
直接取出每个字母出现的次数,每个出现次数非0的值看看能否平均分布就可

class Solution {
    public boolean makeEqual(String[] words) {
        int[] array = new int[26];
        for (String word : words) {
            for (int i = 0; i < word.length(); i++) {
                array[word.charAt(i) - 'a']++;
            }
        }
        int len = words.length;
        for (int i = 0; i < array.length; i++) {
            if(array[i] % len != 0){
                return false;
            }
        }
        return true;
    }
}

第二题 1898. 可移除字符的最大数目(中等)

给你两个字符串 s 和 p ,其中 p 是 s 的一个 子序列 。同时,给你一个元素 互不相同 且下标 从 0 开始 计数的整数数组 removable ,该数组是 s 中下标的一个子集(s 的下标也 从 0 开始 计数)。
请你找出一个整数 k(0 <= k <= removable.length),选出 removable 中的 前 k 个下标,然后从 s 中移除这些下标对应的 k 个字符。整数 k 需满足:在执行完上述步骤后, p 仍然是 s 的一个 子序列 。更正式的解释是,对于每个 0 <= i < k ,先标记出位于 s[removable[i]] 的字符,接着移除所有标记过的字符,然后检查 p 是否仍然是 s 的一个子序列。
返回你可以找出的 最大 k ,满足在移除字符后 p 仍然是 s 的一个子序列。
字符串的一个 子序列 是一个由原字符串生成的新字符串,生成过程中可能会移除原字符串中的一些字符(也可能不移除)但不改变剩余字符之间的相对顺序。
提示:
1 <= p.length <= s.length <= 105
0 <= removable.length < s.length
0 <= removable[i] < s.length
p 是 s 的一个 子字符串
s 和 p 都由小写英文字母组成
removable 中的元素 互不相同

当时思路

比赛时先做的第三题,剩下差不多一个小时在这个题目上面,一直没做出来,当时想判断p是否是s的子序列的复杂度为p.length*s.length(这是子串,不是子序列!),这最大到了十亿,只是一次检查就这么多,所以认为此路不通,导致一直没有思路,甚至想到的唯一办法,用dp遍历一次将所有子序列求出来,然后计算每个字母出现次数,再一个个减去removable对应的字母,如果字母出现次数不为0则仍然存在子序列,只是dp的复杂度也没办法降低,更何况空间复杂度也会很高。

正确思路

实际检查子序列的复杂度最大为m+n,所以如果直接暴力,复杂度为removable.length * (p.length + s.length),显然removable这里可以使用二分法,则将复杂度降低为log removable.length * (p.length + s.length)
代码如下,本身可以不用判断reset,只是考虑到取二段后,去标记字符有一段是重复的,例如第一次标记了0-mid间的数,然后取左半段,那么其中只需要清除左半段中的右半部分的标记,减少运算次数,整体仍然是二分的思想,中间使用origin赋值,尽量减少在循环里创建对象,减少内存使用(或者说是内存抖动)

    public int maximumRemovals(String s, String p, int[] removable) {
        char[] sChars = s.toCharArray();
        char[] origin = sChars.clone();
        char[] pChars = p.toCharArray();
        int left = 0;
        int right = removable.length;
        boolean reset = false;
        while (left < right) {
            int mid = ((right - left) >> 1) + left;
            if(reset){
                for (int i = mid + 1; i <= right; i++) {
                    sChars[removable[i]] = origin[removable[i]];
                }
            }else {
                for (int i = left; i <= mid; i++) {
                    sChars[removable[i]] = '#';
                }
            }
            if ((reset = check(sChars, pChars))) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }

第三题 1899. 合并若干三元组以形成目标三元组(中等)

三元组 是一个由三个整数组成的数组。给你一个二维整数数组 triplets ,其中 triplets[i] = [ai, bi, ci] 表示第 i 个 三元组 。同时,给你一个整数数组 target = [x, y, z] ,表示你想要得到的 三元组 。
为了得到 target ,你需要对 triplets 执行下面的操作 任意次(可能 零 次):
选出两个下标(下标 从 0 开始 计数)i 和 j(i != j),并 更新 triplets[j] 为 [max(ai, aj), max(bi, bj), max(ci, cj)] 。
例如,triplets[i] = [2, 5, 3] 且 triplets[j] = [1, 7, 5],triplets[j] 将会更新为 [max(2, 1), max(5, 7), max(3, 5)] = [2, 7, 5] 。
如果通过以上操作我们可以使得目标 三元组 target 成为 triplets 的一个 元素 ,则返回 true ;否则,返回 false 。
提示:
1 <= triplets.length <= 105
triplets[i].length == target.length == 3
1 <= ai, bi, ci, x, y, z <= 1000

当时思路

先仔细看题,思路还是比较容易,只需要取出所有三元素小于等于目标三元素的值,全部取最大值即可
以下是当时的解法,时间复杂度上就是o(n) 空间复杂度o(1) 所以从算法上是最优解法,细节仍然可以优化

class Solution {
    public boolean mergeTriplets(int[][] triplets, int[] target) {
        int[] temp = new int[3];
        int len = triplets.length;
        for (int i = 0; i < len; i++) {
            boolean flag = true;
            for (int j = 0; j < 3; j++) {
                if (triplets[i][j] > target[j]) {
                    flag = false;
                    break;
                }
            }
            if(flag){
                for (int j = 0; j < 3; j++) {
                    temp[j] = Math.max(temp[j], triplets[i][j]);
                }
            }
        }
        for (int i = 0; i < 3; i++) {
            if (temp[i] != target[i]) {
                return false;
            }
        }
        return true;
    }
}

优化思路

在flag的判断中可以加上只有当triplets[i]有起码一个到目的值才进入
在判断的里面,可以改为布尔数组,达到值就可以不做判断直接跳过,这样结果的判断也只需要三个值做且运算
以下为LeetCode中的最优算法,细节做了足够的优化

class Solution {
    public boolean check(int[][]m,int[]c,int idx){
        for(int i=0;i

总结

第二题的问题太大了,子串子序列的复杂度就随便想一下,导致后面一个小时挂在这
做的比较好的点是第二题初步计算复杂度认为没思路就看了第三题,迅速完成了第三题。
整体来说本周周赛难度较小,除了第四题,暂时没到第四题的水平

你可能感兴趣的:(LeetCode第245场周赛(6.13)总结)