第 58 场力扣夜喵双周赛

两道600多

5193. 删除字符使字符串变好

题目描述

一个字符串如果没有 三个连续 相同字符,那么它就是一个 好字符串 。

给你一个字符串 s ,请你从 s 删除 最少 的字符,使它变成一个 好字符串 。

请你返回删除后的字符串。题目数据保证答案总是 唯一的 。

示例 1:

输入:s = “leeetcode”
输出:“leetcode”
解释:
从第一组 ‘e’ 里面删除一个 ‘e’ ,得到 “leetcode” 。
没有连续三个相同字符,所以返回 “leetcode” 。

示例 2:

输入:s = “aaabaaaa”
输出:“aabaa”
解释:
从第一组 ‘a’ 里面删除一个 ‘a’ ,得到 “aabaaaa” 。
从第二组 ‘a’ 里面删除两个 ‘a’ ,得到 “aabaa” 。
没有连续三个相同字符,所以返回 “aabaa” 。

示例 3:

输入:s = “aab”
输出:“aab”
解释:没有连续三个相同字符,所以返回 “aab” 。

提示:

1 <= s.length <= 105
s 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-characters-to-make-fancy-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

四分钟写出来的,还行吧

class Solution {public String makeFancyString(String s) {int l = s.length();StringBuffer sb = new StringBuffer();for(int i = 0; i < l; i++){char temp = s.charAt(i);if(i > 1 && temp == s.charAt(i - 1) && temp == s.charAt(i - 2)){continue;}sb.append(temp);}return sb.toString();}
}

5827. 检查操作是否合法

题目描述

给你一个下标从 0 开始的 8 x 8 网格 board ,其中 board[r][c] 表示游戏棋盘上的格子 (r, c) 。棋盘上空格用 ‘.’ 表示,白色格子用 ‘W’ 表示,黑色格子用 ‘B’ 表示。

游戏中每次操作步骤为:选择一个空格子,将它变成你正在执行的颜色(要么白色,要么黑色)。但是,合法 操作必须满足:涂色后这个格子是 好线段的一个端点 (好线段可以是水平的,竖直的或者是对角线)。

好线段 指的是一个包含 三个或者更多格子(包含端点格子)的线段,线段两个端点格子为 同一种颜色 ,且中间剩余格子的颜色都为 另一种颜色 (线段上不能有任何空格子)。你可以在下图找到好线段的例子:

给你两个整数 rMove 和 cMove 以及一个字符 color ,表示你正在执行操作的颜色(白或者黑),如果将格子 (rMove, cMove) 变成颜色 color 后,是一个 合法 操作,那么返回 true ,如果不是合法操作返回 false 。

示例 1:

输入:board = [[".",".",".",“B”,".",".",".","."],[".",".",".",“W”,".",".",".","."],[".",".",".",“W”,".",".",".","."],[".",".",".",“W”,".",".",".","."],[“W”,“B”,“B”,".",“W”,“W”,“W”,“B”],[".",".",".",“B”,".",".",".","."],[".",".",".",“B”,".",".",".","."],[".",".",".",“W”,".",".",".","."]], rMove = 4, cMove = 3, color = “B”
输出:true
解释:’.’,‘W’ 和 ‘B’ 分别用颜色蓝色,白色和黑色表示。格子 (rMove, cMove) 用 ‘X’ 标记。
以选中格子为端点的两个好线段在上图中用红色矩形标注出来了。

示例 2:

输入:board = [[".",".",".",".",".",".",".","."],[".",“B”,".",".",“W”,".",".","."],[".",".",“W”,".",".",".",".","."],[".",".",".",“W”,“B”,".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",“B”,“W”,".","."],[".",".",".",".",".",".",“W”,"."],[".",".",".",".",".",".",".",“B”]], rMove = 4, cMove = 4, color = “W”
输出:false
解释:虽然选中格子涂色后,棋盘上产生了好线段,但选中格子是作为中间格子,没有产生以选中格子为端点的好线段。

提示:

board.length == board[r].length == 8
0 <= rMove, cMove < 8
board[rMove][cMove] == ‘.’
color 要么是 ‘B’ 要么是 ‘W’ 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/check-if-move-is-legal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我当时没多想,就是上下左右以及四个角方向,复制了八份代码写的:
每个方向,如果碰到了空白,就不可能有好线段;如果第二个就碰到了相同的颜色,也不可能;如果能走到后面且后面有相同的颜色,就返回true

class Solution {public boolean checkMove(char[][] board, int rMove, int cMove, char color) {//意思是从这个点变色以后,有以这个点为端点的号线段int m = board.length;if(m == 0)return false;int n = board[0].length;if(n == 0)return false;//左for(int i = cMove - 1; i >= 0; i--){if(!inarea(rMove, i, m, n))break;if(board[rMove][i] == '.')break;if(board[rMove][i] == color && cMove - i > 1){return true;   }if(board[rMove][i] == color)break;}//右for(int i = cMove + 1; i < n; i++){if(!inarea(rMove, i, m, n))break;if(board[rMove][i] == '.')break;if(board[rMove][i] == color && i - cMove > 1){return true;   }if(board[rMove][i] == color)break;}//上for(int i = rMove - 1; i >= 0; i--){if(!inarea(i, rMove, m, n))break;if(board[i][cMove] == '.')break;if(board[i][cMove] == color && rMove - i > 1){return true;   }if(board[i][cMove] == color)break;}//下for(int i = rMove + 1; i < m; i++){if(!inarea(i, rMove, m, n))break;if(board[i][cMove] == '.')break;if(board[i][cMove] == color && i - rMove > 1){return true;   }if(board[i][cMove] == color)break;}//左上for(int i = 1; i < Math.max(m, n); i++){int row = rMove - i;int col = cMove - i;if(!inarea(row, col, m, n))break;if(board[row][col] == '.')break;if(board[row][col] == color && i > 1){return true;   }if(board[row][col] == color)break;}//左下for(int i = 1; i < Math.max(m, n); i++){int row = rMove + i;int col = cMove - i;if(!inarea(row, col, m, n))break;if(board[row][col] == '.')break;if(board[row][col] == color && i > 1){return true;   }if(board[row][col] == color)break;}//右下for(int i = 1; i < Math.max(m, n); i++){int row = rMove + i;int col = cMove + i;if(!inarea(row, col, m, n))break;if(board[row][col] == '.')break;if(board[row][col] == color && i > 1){return true;   }if(board[row][col] == color)break;}//右上for(int i = 1; i < Math.max(m, n); i++){int row = rMove - i;int col = cMove + i;if(!inarea(row, col, m, n))break;if(board[row][col] == '.')break;if(board[row][col] == color && i > 1){return true;   }if(board[row][col] == color)break;}return false;}public boolean inarea(int x, int y, int m, int n){return x >= 0 && x < m && y >= 0 && y < n;}
}

5828. K 次调整数组大小浪费的最小总空间

题目描述

你正在设计一个动态数组。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是 i 时刻数组中的元素数目。除此以外,你还有一个整数 k ,表示你可以 调整 数组大小的 最多 次数(每次都可以调整成 任意 大小)。

t 时刻数组的大小 sizet 必须大于等于 nums[t] ,因为数组需要有足够的空间容纳所有元素。t 时刻 浪费的空间 为 sizet - nums[t] ,总 浪费空间为满足 0 <= t < nums.length 的每一个时刻 t 浪费的空间 之和 。

在调整数组大小不超过 k 次的前提下,请你返回 最小总浪费空间 。

注意:数组最开始时可以为 任意大小 ,且 不计入 调整大小的操作次数。

示例 1:

输入:nums = [10,20], k = 0
输出:10
解释:size = [20,20].
我们可以让数组初始大小为 20 。
总浪费空间为 (20 - 10) + (20 - 20) = 10 。

示例 2:

输入:nums = [10,20,30], k = 1
输出:10
解释:size = [20,20,30].
我们可以让数组初始大小为 20 ,然后时刻 2 调整大小为 30 。
总浪费空间为 (20 - 10) + (20 - 20) + (30 - 30) = 10 。

示例 3:

输入:nums = [10,20,15,30,20], k = 2
输出:15
解释:size = [10,20,20,30,30].
我们可以让数组初始大小为 10 ,时刻 1 调整大小为 20 ,时刻 3 调整大小为 30 。
总浪费空间为 (10 - 10) + (20 - 20) + (20 - 15) + (30 - 30) + (30 - 20) = 15 。

提示:

1 <= nums.length <= 200
1 <= nums[i] <= 106
0 <= k <= nums.length - 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-total-space-wasted-with-k-resizing-operations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

隐约感受到了动态规划的力量,其实状态都想好了,但是还是没规出来,动态规划要好好再练一次了!

实际上就是在区间里选k个位置,然后保证被分成的k+1个区间每个区间数组的大小 sizet都是各自的最大值,这个也想到了
dp[i][j]表示到第i个位置,总共分成j段的浪费最小值,具体看注释

改了半天终于对了
也可以从前往后递推,即枚举第j + 1个位置

class Solution {public int minSpaceWastedKResizing(int[] nums, int k) {//做一下int l = nums.length;//首先预处理每一段的最大值,和如果赋予这一段这个最大值,浪费空间是多少int[][] lost = new int[l][l];for(int i = 0; i < l; i++){int max = 0;int pre = 0;for(int j = i; j < l; j++){pre = pre + nums[j];max = Math.max(max, nums[j]);lost[i][j] = max * (j - i + 1) - pre;//System.out.print(lost[i][j] + " ");}//System.out.println();}//dp[i][j]表示到第i个位置,总共分成了j段浪费的最小值//调整了k次会分成k+1段int[][] dp = new int[l + 1][k + 2];//初始化,赋最大值//0位置分成0份,为0for(int i = 1; i <= l; i++){Arrays.fill(dp[i], Integer.MAX_VALUE / 2);}//怎么转移呢,这个不会//枚举最后一段的长度,这个是怎么想到的呢//我记得当时做题的时候,我想的是从dp[i - 1][j] 和 dp[i][j- 1]转移的,然后不知道该怎么转移//如果从dp[i - 1][j - 1]转移的话,就表示之前i - 1位置分成j - 1段,如果要多分一段,就枚举这一段的长度//得到dp[i][j]for(int i = 1; i <= l; i++){for(int j = 1; j <= k + 1; j++){//枚举i - 1这一段的长度,也就是这一段可以从0开始到i//相当于把之前t的长度分成了j - 1 份,然后把剩下的t 到 i- 1 分成了一份for(int t = 1; t <= i; t++){dp[i][j] = Math.min(dp[i][j], dp[t - 1][j - 1] + lost[t - 1][i - 1]);}}}return dp[l][k + 1];}
}

5220. 两个回文子字符串长度的最大乘积

题目描述

给你一个下标从 0 开始的字符串 s ,你需要找到两个 不重叠的回文 子字符串,它们的长度都必须为 奇数 ,使得它们长度的乘积最大。

更正式地,你想要选择四个整数 i ,j ,k ,l ,使得 0 <= i <= j < k <= l < s.length ,且子字符串 s[i…j] 和 s[k…l] 都是回文串且长度为奇数。s[i…j] 表示下标从 i 到 j 且 包含 两端下标的子字符串。

请你返回两个不重叠回文子字符串长度的 最大 乘积。

回文字符串 指的是一个从前往后读和从后往前读一模一样的字符串。子字符串 指的是一个字符串中一段连续字符。

示例 1:

输入:s = “ababbb”
输出:9
解释:子字符串 “aba” 和 “bbb” 为奇数长度的回文串。乘积为 3 * 3 = 9 。

示例 2:

输入:s = “zaaaxbbby”
输出:9
解释:子字符串 “aaa” 和 “bbb” 为奇数长度的回文串。乘积为 3 * 3 = 9 。

提示:

2 <= s.length <= 10^5
s 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-of-the-length-of-two-palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

看到数据范围,就知道肯定得马拉车,然后忘的一干二净了
首先再学习马拉车算法,看之前做的最长回文子串中自己写的理解
其实想通了也很好写,主要就是充分利用回文的特性
下面为LeetCode5.最长回文子串中写的马拉车算法:

class Solution {//在我心里,weiwei哥基本就是那个男人,tmd太详细了,想看不懂都难啊//这个Manacher算法,看了官解,一脸懵逼,不知道在说什么,//这里总结一下它的主要思想,就是构造一个新的字符串,新字符串在原字符串每一个字符之间加了一个新的符号,左右也要加//使得整个字符串的长度边为2*len+1//然后也是中心扩散的思想,用一个数组记录所有中心最长的扩散距离p[]//这个计算出来的最大的p[i]就是最长回文串的长度//然后呢,如何降低复杂度,这就是难理解的地方了//用一个maxRight和一个center分别记录当前能扩散到的最右边的地方和对应的中心//从左到右填p[],当i>=maxRight的时候,就用中心扩散的方法进行填表//当i<maxRight的时候,i关于center的中心位置mirror,还有maxRight关于center的中心位置这几个点决定了如何更新p[]//当maxRight - i > p[mirror]的时候,因为关于center是中心对称的,因此p[i]=p[mirror]//当maxRight - i = p[mirror]的时候,此时因为maxRight以后 左边没有相应的参照点了,因此还需要用中心扩散继续扩大maxRight//当maxRight - i < p[mirror]的时候,此时p[i]在扩展到maxRight,到这里,是对称的,//maxRight以后,如果还能继续扩展,那么p[i]的左端,center的右端肯定有一个与之对称,在镜像过去,肯定等于maxLeft左边的值,//那么,这个maxRight就不对了,因此不可以继续扩展(也能从左到右思考,找出矛盾)//这样就可以完整的把表填好了public String longestPalindrome(String s) {// 特判int len = s.length();if (len < 2) {return s;}// 得到预处理字符串String str = addBoundaries(s, '#');// 新字符串的长度int sLen = 2 * len + 1;// 数组 p 记录了扫描过的回文子串的信息int[] p = new int[sLen];// 双指针,它们是一一对应的,须同时更新int maxRight = 0;       //对应目前遍历过的回文串最右边的下标int center = 0;         //对应的中心位置// 当前遍历的中心最大扩散步数,其值等于原始字符串的最长回文子串的长度int maxLen = 1;// 原始字符串的最长回文子串的起始位置,与 maxLen 必须同时更新        int start = 0;for (int i = 0; i < sLen; i++) {//如果当前位置下标在maxRight内,那么可以根据回文串的性质减少计算if (i < maxRight) {//首先计算i关于中心对称的下标mirrorint mirror = 2 * center - i;// 这一行代码是 Manacher 算法的关键所在,要结合图形来理解// 如果maxRight - i > p[mirror],那么说明扩散不到maxRight外,因此直接p[i] = p[mirror]//如果等于,那么就先取p[mirror]的值,然后再中心扩散//如果小于,首先肯定能取到maxRight - i,如果还能往后继续扩散的话,会和左边相同,从而使maxRight的长度增加,所以肯定不能扩散了,所以取maxRight - i//总结,取两者的最小值p[i] = Math.min(maxRight - i, p[mirror]);}// 下一次尝试扩散的左右起点,能扩散的步数直接加到 p[i] 中//取到当前p[i]以后,中心扩散,如果还能扩散就继续int left = i - (1 + p[i]);int right = i + (1 + p[i]);// left >= 0 && right < sLen 保证不越界// str.charAt(left) == str.charAt(right) 表示可以扩散 1 次while (left >= 0 && right < sLen && str.charAt(left) == str.charAt(right)) {p[i]++;left--;right++;}// 根据 maxRight 的定义,它是遍历过的 i 的 i + p[i] 的最大者// 如果 maxRight 的值越大,进入上面 i < maxRight 的判断的可能性就越大,这样就可以重复利用之前判断过的回文信息了if (i + p[i] > maxRight) {// maxRight 和 center 需要同时更新maxRight = i + p[i];center = i;}if (p[i] > maxLen) {// 记录最长回文子串的长度和相应它在原始字符串中的起点maxLen = p[i];start = (i - maxLen) / 2;}}return s.substring(start, start + maxLen);}/*** 创建预处理字符串** @param s      原始字符串* @param divide 分隔字符* @return 使用分隔字符处理以后得到的字符串*/private String addBoundaries(String s, char divide) {int len = s.length();if (len == 0) {return "";}if (s.indexOf(divide) != -1) {throw new IllegalArgumentException("参数错误,您传递的分割字符,在输入字符串中存在!");}StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < len; i++) {stringBuilder.append(divide);stringBuilder.append(s.charAt(i));}stringBuilder.append(divide);return stringBuilder.toString();}
}

通过马拉车,可以在O(n)的复杂度内,求得每一个位置最长回文串长度

然后就是要找出两个长度为m,n的位置,他们代表的子串不重叠,并且长度之积最大
这个也不好想

学习零神的做法:
https://leetcode-cn.com/problems/maximum-product-of-the-length-of-two-palindromic-substrings/solution/liang-ge-hui-wen-zi-zi-fu-chuan-chang-du-soyb/
pre[i]代表0到i位置的最长回文子串的长度,同理suf[i]代表后缀,然后遍历每个位置i,把前后缀相乘
计算前缀的时候,很容易想到的方法就是从前往后遍历i,然后更新回文串结尾位置对应的pre[]值,这样,求出了以每个位置i结尾的最长回文串的长度
然后,将pre[]中相邻两个位置的值相比较,取较大者,得到最终的前缀数组pre[]

但是这样得到的前缀数组会有一个问题,zyxw…cbabc…wxyz这样的字符串,pre[]数组只有最后一个位置有值,其他位置都是1
而实际上,这样的字符串,在后半部分,每一个位置都应该+1;导致这个问题,是因为在a出现的位置,只记录了最长的回文串的长度26,而其他位置都没被记录

所以需要再反着遍历一遍,将 pre[i] 更新为其与 pre[i+1]−2 的较大值即可。其妙处就在于:如果以位置 i+1 为右边界,有一个长度为 pre[i+1] 的回文串,那么以位置 i 为右边界,就有一个长度为 pre[i+1]−2 的回文串,也就是将前者的首尾两个字符去掉。

写一下代码:
注意最后取long,并且pre和suf的下标不同

class Solution {public long maxProduct(String s) {//首先马拉车计算每个位置最长回文子串的长度//因为求的是奇数,所以就不需要填充了int l = s.length();int[] p = new int[l];int maxRight = 0;int center = 0;for(int i = 0; i < l; i++){//马拉车的关键if(i < maxRight){int mirror = 2 * center - i;p[i] = Math.min(maxRight - i, p[mirror]);}//中心扩散int left = i - p[i];int right = i  + p[i];while(left >= 0 && right < l && s.charAt(left) == s.charAt(right)){p[i]++;left--;right++;}if(p[i] + i > maxRight){maxRight = p[i] + i - 1;center = i;}//System.out.print(p[i] + " ");}//System.out.println();//到这里求出了每个位置最长的回文串长度//然后遍历每个分割线,求出左右最长的回文子串int[] pre = new int[l];int[] suf = new int[l];//先求以每个位置为结尾和开头的最长回文子串长度for(int i = 0; i < l; i++){pre[p[i] + i - 1] = Math.max(pre[p[i] + i - 1], p[i] * 2 - 1);           suf[i - p[i] + 1] = Math.max(suf[i - p[i] + 1], p[i] * 2 - 1);}/*for(int i = 0; i < l; i++){System.out.print(pre[i] + " ");}System.out.println();*///再求前后缀for(int i = 1; i < l; i++){pre[i] = Math.max(pre[i], pre[i - 1]);}for(int i = l - 2; i >= 0; i--){suf[i] = Math.max(suf[i], suf[i + 1]);}//因为中间的位置没有进行更新,所以更新中间位置for(int i = 1; i < l; i++){suf[i] = Math.max(suf[i], suf[i - 1] - 2);}for(int i = l - 2; i >= 0; i--){pre[i] = Math.max(pre[i], pre[i + 1] - 2);}/*for(int i = 0; i < l; i++){System.out.print(pre[i] + " ");}System.out.println();for(int i = 0; i < l; i++){System.out.print(suf[i] + " ");}*/long res = 0;//遍历每个分割线for(int i = 0; i < l - 1; i++){res = Math.max(res, (long)pre[i] * suf[i + 1]);}return res;}
}

第 253 场力扣周赛

第一次A了4道,最后一道LIS本来第一次就写对了,非要再那瞎想,搞得多整了半个小时,也是醉了,700多

1961. 检查字符串是否为数组前缀

题目描述

给你一个字符串 s 和一个字符串数组 words ,请你判断 s 是否为 words 的 前缀字符串 。

字符串 s 要成为 words 的 前缀字符串 ,需要满足:s 可以由 words 中的前 k(k 为 正数 )个字符串按顺序相连得到,且 k 不超过 words.length 。

如果 s 是 words 的 前缀字符串 ,返回 true ;否则,返回 false 。

示例 1:

输入:s = “iloveleetcode”, words = [“i”,“love”,“leetcode”,“apples”]
输出:true
解释:
s 可以由 “i”、“love” 和 “leetcode” 相连得到。

示例 2:

输入:s = “iloveleetcode”, words = [“apples”,“i”,“love”,“leetcode”]
输出:false
解释:
数组的前缀相连无法得到 s 。

提示:

1 <= words.length <= 100
1 <= words[i].length <= 20
1 <= s.length <= 1000
words[i] 和 s 仅由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/check-if-string-is-a-prefix-of-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

一直拼接,直到拼接一样了

class Solution {public boolean isPrefixString(String s, String[] words) {int ls = s.length();int n = words.length;StringBuffer sb = new StringBuffer();for(int i = 0; i < n; i++){sb.append(words[i]);System.out.println(sb.toString());if(sb.toString().equals(s))return true;}return false;}
}

1962. 移除石子使总数最小

题目描述

给你一个整数数组 piles ,数组 下标从 0 开始 ,其中 piles[i] 表示第 i 堆石子中的石子数量。另给你一个整数 k ,请你执行下述操作 恰好 k 次:

选出任一石子堆 piles[i] ,并从中 移除 floor(piles[i] / 2) 颗石子。
注意:你可以对 同一堆 石子多次执行此操作。

返回执行 k 次操作后,剩下石子的 最小 总数。

floor(x) 为 小于 或 等于 x 的 最大 整数。(即,对 x 向下取整)。

示例 1:

输入:piles = [5,4,9], k = 2
输出:12
解释:可能的执行情景如下:
-对第 2 堆石子执行移除操作,石子分布情况变成 [5,4,5] 。
-对第 0 堆石子执行移除操作,石子分布情况变成 [3,4,5] 。
剩下石子的总数为 12 。

示例 2:

输入:piles = [4,3,6,7], k = 3
输出:12
解释:可能的执行情景如下:
-对第 2 堆石子执行移除操作,石子分布情况变成 [4,3,3,7] 。
-对第 3 堆石子执行移除操作,石子分布情况变成 [4,3,3,4] 。
-对第 0 堆石子执行移除操作,石子分布情况变成 [2,3,3,4] 。
剩下石子的总数为 12 。

提示:

1 <= piles.length <= 105
1 <= piles[i] <= 104
1 <= k <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-stones-to-minimize-the-total
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

每次移除最多的那堆石子的一半

class Solution {public int minStoneSum(int[] piles, int k) {int l = piles.length;PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> (b - a));for(int i = 0; i < l; i++){pq.offer(piles[i]);}int res = 0;while(!pq.isEmpty() && k > 0){int t = pq.poll();int cut = t / 2;pq.offer(t - cut);k--;}while(!pq.isEmpty())res += pq.poll();return res;}
}

1963. 使字符串平衡的最小交换次数

题目描述

给你一个字符串 s ,下标从 0 开始 ,且长度为偶数 n 。字符串 恰好 由 n / 2 个开括号 ‘[’ 和 n / 2 个闭括号 ‘]’ 组成。

只有能满足下述所有条件的字符串才能称为 平衡字符串 :

字符串是一个空字符串,或者
字符串可以记作 AB ,其中 A 和 B 都是 平衡字符串 ,或者
字符串可以写成 [C] ,其中 C 是一个 平衡字符串 。
你可以交换 任意 两个下标所对应的括号 任意 次数。

返回使 s 变成 平衡字符串 所需要的 最小 交换次数。

示例 1:

输入:s = “][][”
输出:1
解释:交换下标 0 和下标 3 对应的括号,可以使字符串变成平衡字符串。
最终字符串变成 “[[]]” 。

示例 2:

输入:s = “]]][[[”
输出:2
解释:执行下述操作可以使字符串变成平衡字符串:
-交换下标 0 和下标 4 对应的括号,s = “[]][[]” 。
-交换下标 1 和下标 5 对应的括号,s = “[[][]]” 。
最终字符串变成 “[[][]]” 。

示例 3:

输入:s = “[]”
输出:0
解释:这个字符串已经是平衡字符串。

提示:

n == s.length
2 <= n <= 106
n 为偶数
s[i] 为’[’ 或 ‘]’
开括号 ‘[’ 的数目为 n / 2 ,闭括号 ‘]’ 的数目也是 n / 2

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-number-of-swaps-to-make-the-string-balanced
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我是这样想的,左右两个栈,配对就跳过,如果左边发现多了’]’,右边发现多了’[’
然后就需要交换

class Solution {public int minSwaps(String s) {int l = s.length();//刚开始肯定是左括号,//双指针int left = 0;int right = l - 1;char[] cc = s.toCharArray();Stack<Character> stack1 = new Stack<>();Stack<Character> stack2 = new Stack<>();int res = 0;while(left < right){while(left < right){if(cc[left] == '['){stack1.push('[');}if(cc[left] == ']'){if(stack1.isEmpty()){break;}else{stack1.pop();}}left++;}while(left < right){if(cc[right] == ']'){stack2.push(']');}if(cc[right] == '['){if(stack2.isEmpty()){break;}else{stack2.pop();}}right--;}if(left < right)res++;left++;stack1.push('[');right--;stack2.push(']');}return res;}
}

大佬们都是计数
如果,不匹配了,那么计数要加2,怎么理解呢,左边右括号变成左括号,即-1变成1,count要加2

class Solution {public int minSwaps(String s) {char[] cc = s.toCharArray();int count = 0;int res = 0;for(char c : cc){if(c == '['){count++;}else{count--;}//如果不匹配了,就交换if(count < 0){res++;count += 2;}}return res;}
}

1964. 找出到每个位置为止最长的有效障碍赛跑路线

题目描述

你打算构建一些障碍赛跑路线。给你一个 下标从 0 开始 的整数数组 obstacles ,数组长度为 n ,其中 obstacles[i] 表示第 i 个障碍的高度。

对于每个介于 0 和 n - 1 之间(包含 0 和 n - 1)的下标 i ,在满足下述条件的前提下,请你找出 obstacles 能构成的最长障碍路线的长度:

你可以选择下标介于 0 到 i 之间(包含 0 和 i)的任意个障碍。
在这条路线中,必须包含第 i 个障碍。
你必须按障碍在 obstacles 中的 出现顺序 布置这些障碍。
除第一个障碍外,路线中每个障碍的高度都必须和前一个障碍 相同 或者 更高 。
返回长度为 n 的答案数组 ans ,其中 ans[i] 是上面所述的下标 i 对应的最长障碍赛跑路线的长度。

示例 1:

输入:obstacles = [1,2,3,2]
输出:[1,2,3,3]
解释:每个位置的最长有效障碍路线是:
-i = 0: [1], [1] 长度为 1
-i = 1: [1,2], [1,2] 长度为 2
-i = 2: [1,2,3], [1,2,3] 长度为 3
-i = 3: [1,2,3,2], [1,2,2] 长度为 3

示例 2:

输入:obstacles = [2,2,1]
输出:[1,2,1]
解释:每个位置的最长有效障碍路线是:
-i = 0: [2], [2] 长度为 1
-i = 1: [2,2], [2,2] 长度为 2
-i = 2: [2,2,1], [1] 长度为 1

示例 3:

输入:obstacles = [3,1,5,6,4,2]
输出:[1,1,2,3,2,2]
解释:每个位置的最长有效障碍路线是:
-i = 0: [3], [3] 长度为 1
-i = 1: [3,1], [1] 长度为 1
-i = 2: [3,1,5], [3,5] 长度为 2, [1,5] 也是有效的障碍赛跑路线
-i = 3: [3,1,5,6], [3,5,6] 长度为 3, [1,5,6] 也是有效的障碍赛跑路线
-i = 4: [3,1,5,6,4], [3,4] 长度为 2, [1,4] 也是有效的障碍赛跑路线
-i = 5: [3,1,5,6,4,2], [1,2] 长度为 2

提示:

n == obstacles.length
1 <= n <= 105
1 <= obstacles[i] <= 107

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-longest-valid-obstacle-course-at-each-position
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

最长递增子序列问题
这里不是严格递增的,而是可以相等的
所以我在写完这个代码以后,一直在纠结,如果后面插入的数和之前数相同的话,该怎么办
但是最后想明白了,如果插入的数和当前最后一个数,也就是最大的数相同,那么就插到末尾,也就是idx的位置
如果插入的数和所有数都不一样,那么就插入中间,即替换掉比它大的最小数
那么如果插入的数和其中一个数一样怎么办呢(不是末尾),其实也是替换比它大的第一个数就可以了(因为不在末尾,所以肯定有比它大的数)

所以只需要在原来最长递增子序列的基础上,加一个等于的判断就可以了

class Solution {public int[] longestObstacleCourseAtEachPosition(int[] obstacles) {//找以当前位置为结束的最长递增子序列int l = obstacles.length;int[] res = new int[l];int[] dp = new int[l + 1];int idx = 0;dp[++idx] = obstacles[0];res[0] = 1;for(int i = 1; i < l; i++){int temp = obstacles[i];if(temp >= dp[idx]){dp[++idx] = temp;res[i] = idx;continue;} int left = 1;int right = idx;while(left < right){int mid = (right - left) / 2 + left;if(dp[mid] > temp){right = mid;}else{left = mid + 1;}}dp[left] = temp;res[i] = left;}return res;}
}

LeetCode 第 58 场力扣夜喵双周赛(动态规划、马拉车算法,前后缀处理)/ 第 253 场力扣周赛(贪心,LIS)相关推荐

  1. LeetCode第 57 场力扣夜喵双周赛(差分数组、单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)

    LeetCode第 57 场力扣夜喵双周赛 离knight勋章越来越近,不过水平没有丝毫涨进 1941. 检查是否所有字符出现次数相同 题目描述 给你一个字符串 s ,如果 s 是一个 好 字符串,请 ...

  2. LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉、动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))

    第 59 场力扣夜喵双周赛 两道400多五百,后两道都写出代码来了,但是都有问题,哭辽- 还有刚开始第一道测试好慢,搞心态了 5834. 使用特殊打字机键入单词的最少时间 有一个特殊打字机,它由一个 ...

  3. 【回文串1 动态规划 马拉车算法】LeetCode 5. Longest Palindromic Substring

    LeetCode 5. Longest Palindromic Substring LeetCode中与回文串相关的免费的题目共有15道(5, 9, 125, 131, 132, 214, 234, ...

  4. 通俗易懂:贪心算法(一):分配问题 (力扣455分发饼干 和135分发糖果)

    看完本文,可以顺便解决leetcode以下两个题目: 455.分发饼干(简单) 135.分发糖果(困难) 一.通俗易懂的 贪心算法 |思想 贪心算法就是采用贪心的策略,保证每一次的操作都是局部最优的, ...

  5. 力扣- - 最短回文串(KMP算法)

    力扣- - 最短回文串(KMP算法) 文章目录 力扣- - 最短回文串(KMP算法) 一.题目描述 二.分析之KMP算法 1.暴力法 2.KMP算法 3.next数组求法1:暴力查找最长的前后缀 4. ...

  6. [LeetCode][M0005]最长回文子串(Java)(马拉车(Manacher)算法)

    题目描述: 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab" 注 ...

  7. 【分享】原力计划的初衷 【探讨】新的一年,你对原力计划有哪些期待?

    课前小差 哈喽,大家好,我是几何心凉,这是一份全新的专栏,唯一得倒CSDN王总的授权,来对于我们每周四的绿萝时间 --[直达CSDN]直播内容进行总结概括,让大家能够省去看直播回放的时间也能够了解直播 ...

  8. **【校招速递】入职鹅厂,算法大赛优秀选手开启职场新生活**

    [校招速递]入职鹅厂,算法大赛优秀选手开启职场新生活 一年一度的腾讯广告算法大赛云集了各路优秀算法达人,他们用智慧与技术的碰撞,激发出算法思维的火花.今天我们有幸采访到了徐安同学,在2018腾讯广告算 ...

  9. 斗地主之用蚁群算法整理牌型-如何进行牌力估计

    我们在前面讲到过,各牌手的牌力估计就是我们在用蚁群算法构造最优牌型时的启发性知识.启发性知识其实就是我们利用自己的经验对事物做出的判优性评估,或者说就是对事物价值的判断. 原则上,应用蚁群算法需要用到 ...

最新文章

  1. 采集文件到kafka
  2. 数据结构-“栈”的基本操作
  3. fastai学习:05_pet_breeds Questionnaire
  4. cdh 差异_“Sahara/CDHPlugin”的版本间的差异
  5. 计算机盘不显示桌面,电脑开机后不显示Windows系统桌面怎么办?
  6. 问题 C: 能被3整除吗?
  7. redis 是哪个厂家的_redis 基本数据类型-字符串(String)
  8. Oracle 行转列pivot 、列转行unpivot 的Sql语句总结
  9. 工程物资云平台_SaaS产品设计说明书(PRD)_施工企业工程项目物资材料管理软件系统
  10. 支付业务与技术架构学习总结(1)——完整的支付系统整体架构
  11. 计算机dns无法修改,Win7系统怎么改DNS地址 修改电脑DNS地址教程具体介绍
  12. 第十四周助教心得体会
  13. 北邮计算机专硕在微软实习转正,【微软中国(Microsoft)实习生面试】很随意很简单,毕竟实习生转正比率很低。-看准网...
  14. 2023年北京科技大学机械专硕考研成功上岸经验分享
  15. 引导mysql执行计划_Mysql执行计划详解
  16. 前端H5新增标签和CSS3高级
  17. 3DS动物之森(animal acrossing)游戏记录
  18. 大端和小端的区别和判断
  19. 深度学习入门笔记(六):浅层神经网络
  20. 最新手机枰测出炉 iPhone X仅仅名列第9名

热门文章

  1. 11.位运算符:什么是位运算符???位运算符共有哪七种???位运算符和逻辑运算符的区别是什么???
  2. 考研英语 刘晓艳小作文模板
  3. Java面向对象笔记 • 【第11章 Swing高级应用】
  4. x²-dy²=-1有多少整数解?近30年无人解开的数学难题有答案了
  5. [力扣c++实现] 221. 最大正方形
  6. 马上就2023年了,Go语言成了吗?
  7. 第十六篇 项目整体管理__指导与管理项目执行的依据、工具与技术、成果和监控项目工作的依据、工具与技术、成果
  8. Linux 批量修改文件名和后缀
  9. 【Python安装配置教程】
  10. 如何实现css垂直居中