[Leetcode Weekly Contest]295

链接:LeetCode

[Leetcode]2287. 重排字符形成目标字符串

给你两个下标从 0 开始的字符串 s 和 target 。你可以从 s 取出一些字符并将其重排,得到若干新的字符串。

从 s 中取出字符并重新排列,返回可以形成 target 的 最大 副本数。

遍历即可。

class Solution {
    public int rearrangeCharacters(String s, String target) {
        HashMap<Character, Integer> sCounter = new HashMap<>();
        HashMap<Character, Integer> targetCounter = new HashMap<>();
        for(var ch:s.toCharArray()) {
            sCounter.put(ch, sCounter.getOrDefault(ch, 0)+1);
        }
        for(var ch:target.toCharArray()) {
            targetCounter.put(ch, targetCounter.getOrDefault(ch, 0)+1);
        }
        int res = s.length();
        for(var ch:targetCounter.keySet()) {
            res = Math.min(res, sCounter.getOrDefault(ch,0)/targetCounter.get(ch));
        }
        return res;
    }
}

[Leetcode]2288. 价格减免

句子 是由若干个单词组成的字符串,单词之间用单个空格分隔,其中每个单词可以包含数字、小写字母、和美元符号 '$' 。如果单词的形式为美元符号后跟着一个非负实数,那么这个单词就表示一个价格。
例如 "$100"、"$23" 和 "$6.75" 表示价格,而 "100"、"$" 和 "2$3" 不是。
注意:本题输入中的价格均为整数。

给你一个字符串 sentence 和一个整数 discount 。对于每个表示价格的单词,都在价格的基础上减免 discount% ,并 更新 该单词到句子中。所有更新后的价格应该表示为一个 恰好保留小数点后两位 的数字。
返回表示修改后句子的字符串。

分隔每个单词,判断是否符合要求,减去折扣,把字符串拼回来。

class Solution {
    public String discountPrices(String sentence, int discount) {
        String[] words = sentence.split(" ");
        for(int i=0;i<words.length;++i) {
            if(isPrice(words[i])) words[i] = getDisocunt(words[i], discount);
        }
        String res = "";
        for(int i=0;i<words.length;++i) {
            res += words[i];
            if(i!=words.length-1) res += " ";
        }
        return res;
    }

    public boolean isPrice(String word) {
        int n = word.length();
        if(word.charAt(0) != '$' || n < 2) return false;
        for(int i=1;i<n;i++) {
            if(word.charAt(i) > '9' || word.charAt(i) < '0') return false;
        }
        return true;
    }

    public String getDisocunt(String word, int discount) {
        long price = 0;
        for(int i=1;i<word.length();i++) {
            price  = price * 10 + (word.charAt(i)-'0');
        }
        return String.format("$%.2f", price/100.0*(100-discount));
    }
}

[Leetcode]2289. 使数组按非递减顺序排列

给你一个下标从 0 开始的整数数组 nums 。在一步操作中,移除所有满足 nums[i - 1] > nums[i] 的 nums[i] ,其中 0 < i < nums.length 。

重复执行步骤,直到 nums 变为 非递减 数组,返回所需执行的操作数。

单调栈。
对于每个 \(nums[i]\),如果它被消除,其要么是被 左侧第一个更大的元素 \(nums[j]\) 消除的,要么是下面的情况。
这种情况是当 \(nums[j]\) 消除 \(nums[i]\) 之前,\(nums[j]\) 自身已经被 \(nums[k] (k < j)\) 消除。但是这种情况下,相当于 \(nums[k]\) 代替了 \(nums[j]\) 的位置,无论这种替换发生了多少次,都不会影响最终答案。因此 仍然只需考虑左侧第一大元素 \(nums[j]\) 即可。

我们可以用 单调栈 求左侧第一个更大元素。(如果找不到左侧第一个更大元素,那么它永远不会被消除)

\(nums[i]\)\(nums[j](j < i)\) 消除。那么,位于 j 和 i 之间的元素一定被首先消除,使得 \(nums[j]\)\(nums[i]\) 相邻,然后再是 \(nums[j]\) 消除 \(nums[i]\)。设 \(f[i]\)\(nums[i]\) 被消除所需的轮数,那么 \(f[i] = \displaystyle{\max(f[j+1]\dots f[i-1]) + 1}\) 可以在更新单调栈时同时统计。

我们只需要统计 \([i+1,j-1]\) 中 仍然在单调栈里的元素 的步数中的最大值即可。因为那些不在单调栈里的元素,当它们被 pop 之后,一定会 push 进一个比他们的最大值 max 还大 1 的元素,因此这个被 push 进的元素可以 “代表” 所有被 pop 的元素。

最终的答案就是 \(\max(f[i])\)

class Solution {
    /**
    * [9   1   2   4    3    5    5]
    *
    * 9
    * -----------------------------6
    * ------------------------5
    * -------------4
    * -------------------3
    * --------2
    * ----1
    *
    *---------------------------------
    * 单调递减队列
    *到 1 的时候 9 在队列中 所以 1是 需要被删除的 时间点为 1
    *到 2 的时候 9 1 在队列中,要删除2需要先删除1, 时间点为 1 t(1)+1
    *到 4 的时候 9 2 在队列中,要删除4需要先删除2, 时间点为 2 t(2)+1
    *到 3 的时候 9 4 在队列中,要删除3不需要先删除其他的值,所以删除3的时间点为1
    *到 5 的时候 9 4 3 在队列中,要删除5 需要先删除 4,3,所以删除5的时间点为t(4)+1
    *到 6 的时候 9 5 在队列中,要删除6 需要先删除 5,所以删除6的时间点为t(5)+1
    */
    public int totalSteps(int[] nums) {
        int ans = 0;
        ArrayDeque<int[]> stack = new ArrayDeque<>();
        for (int num : nums) {
            //要删除当前num 的时间点
            int maxT = 0;
            //单调递减队列
            while (!stack.isEmpty() && stack.peek()[0] <= num) {
                //左边小于等于num的值都需要在num被删除之前删掉,此时maxT 为删掉左边这些小与等于num的数的最大时间
                maxT = Math.max(maxT, stack.pop()[1]);
            }
            //如果stack 不为空,所有还有左边还有比num值大的情况,表示num需要被删除,如果为空,说明num不用删除
            if (!stack.isEmpty()) {
                maxT++;
            }
            stack.push(new int[]{num, maxT});
            ans = Math.max(ans, maxT);
        }
        return ans;
    }
}

[Leetcode]2290. 到达角落需要移除障碍物的最小数目

给你一个下标从 0 开始的二维整数数组 grid ,数组大小为 m x n 。每个单元格都是两个值之一:

  • 0 表示一个 空 单元格,
  • 1 表示一个可以移除的 障碍物 。

你可以向上、下、左、右移动,从一个空单元格移动到另一个空单元格。
现在你需要从左上角 (0, 0) 移动到右下角 (m - 1, n - 1) ,返回需要移除的障碍物的 最小 数目。

BFS。
把障碍物当作可以经过的单元格,经过它的代价为 1,空单元格经过的代价为 0。我们可以用 Dijkstra,但还可以用 0-1 BFS 来将时间复杂度优化至 \(O(mn)\)

class Solution {
    public int minimumObstacles(int[][] grid) {
        int n = grid.length, m = grid[0].length;
        int[][] res = new int[n][m];
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                res[i][j] = Integer.MAX_VALUE;
            }
        }
        res[0][0] = 0;
        //Queue<int[]> queue = new PriorityQueue<int[]>((a,b) -> res[a[0]][a[1]]-res[b[0]][b[1]]);
        ArrayDeque<int[]> queue = new ArrayDeque<>();
        queue.add(new int[] {0,0});
        while(!queue.isEmpty()) {
            int[] q = queue.pollFirst();
            int x = q[0], y = q[1];
            for(int i=0;i<4;++i) {
                int new_x = x, new_y = y;
                if(i==0) new_x--;
                else if(i==1) new_x++;
                else if(i==2) new_y--;
                else if(i==3) new_y++;
                if(new_x>=0 && new_x <=n-1 && new_y>=0&&new_y<=m-1 && (res[x][y]+grid[new_x][new_y] < res[new_x][new_y])) {
                    if(grid[new_x][new_y] == 0) queue.offerFirst(new int[]{new_x,new_y});
                    else queue.offerLast(new int[]{new_x,new_y});
                    res[new_x][new_y] = res[x][y]+grid[new_x][new_y];
                }
            }
        }
        return res[n-1][m-1];
    }
}

参考:
LeetCode
LeetCode

posted @ 2022-07-04 20:52  Jamest  阅读(27)  评论(0编辑  收藏  举报