链接: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];
}
}