13-周赛347总结
第三题想太复杂了,贪心想到了真的好简单
第四题思路正确但是代码写不出来,没有想到可以用TreeMap+dp
移除字符串中的尾随零【LC2710】
给你一个用字符串表示的正整数
num
,请你以字符串形式返回不含尾随零的整数num
。
-
思路
要求不含后置0,因此使用指针指向字符串末尾,移动至字符不为0即可
-
实现
class Solution {public String removeTrailingZeros(String num) {int i = num.length() - 1;while (num.charAt(i) == '0'){i--;}return num.substring(0, i + 1);} }
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
- 复杂度
对角线上不同值的数量差【LC2711】
给你一个下标从
0
开始、大小为m x n
的二维矩阵grid
,请你求解大小同样为m x n
的答案矩阵answer
。矩阵
answer
中每个单元格(r, c)
的值可以按下述方式进行计算:
- 令
topLeft[r][c]
为矩阵grid
中单元格(r, c)
左上角对角线上 不同值 的数量。- 令
bottomRight[r][c]
为矩阵grid
中单元格(r, c)
右下角对角线上 不同值 的数量。然后
answer[r][c] = |topLeft[r][c] - bottomRight[r][c]|
。返回矩阵
answer
。矩阵对角线 是从最顶行或最左列的某个单元格开始,向右下方向走到矩阵末尾的对角线。
如果单元格
(r1, c1)
和单元格(r, c)
属于同一条对角线且r1 < r
,则单元格(r1, c1)
属于单元格(r, c)
的左上对角线。类似地,可以定义右下对角线。
模拟
-
思路
枚举每个单元格,使用两个哈希表分别记录其左上角对角线上和右下角对角线元素的值,哈希表的大小即为不同值的数量,求出两个哈希表大小差值的绝对值即可
-
实现
class Solution {public int[][] differenceOfDistinctValues(int[][] grid) {int m = grid.length, n = grid[0].length;int[][] res = new int[m][n];for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){Set<Integer> left = new HashSet<>();Set<Integer> right = new HashSet<>();int x = i - 1, y = j - 1;while (x >= 0 && y >= 0){left.add(grid[x][y]);x--;y--;}x = i + 1;y = j + 1;while (x < m && y < n){right.add(grid[x][y]);x++;y++;}res[i][j] = Math.abs(left.size() - right.size());}}return res;} }
- 复杂度
- 时间复杂度: O ( m n ∗ m i n ( m , n ) ) \mathcal{O}(mn*min(m, n)) O(mn∗min(m,n))
- 空间复杂度: O ( m i n ( m , n ) ) \mathcal{O}(min(m, n)) O(min(m,n))
- 复杂度
前后缀分解
-
思路
从第一行和第一列的每个位置出发,根据对角线枚举每个位置,遍历时通过哈希表统计不同元素个数。从左上角开始遍历时,遍历到
grid[i][j]
时,哈希表的大小就是 t o p L e f t [ i + 1 ] [ j + 1 ] topLeft[i+1][j+1] topLeft[i+1][j+1];从右下角开始遍历时,遍历到grid[i][j]
时,哈希表的大小就是 b o t t o m R i g h t [ i − 1 ] [ j − 1 ] bottomRight[i-1][j-1] bottomRight[i−1][j−1] -
实现
class Solution {public int[][] differenceOfDistinctValues(int[][] grid) {int m = grid.length, n = grid[0].length;int[][] res = new int[m][n];// 从第一行和第一列的每个位置出发,枚举每条对角线for (int k = 0; k < m + n; k++){// i = j + k - n;int minJ = Math.max(n - k, 0);int maxJ = Math.min(m + n - 1 - k, n - 1);Set<Integer> set = new HashSet<>();// 左上for (int j = minJ; j < maxJ; j++){int i = j + k - n;set.add(grid[i][j]);res[i + 1][j + 1] = set.size();}set.clear();// 右下for (int j = maxJ; j > minJ; j--){int i = k + j - n;set.add(grid[i][j]);res[i - 1][j - 1] = Math.abs(res[i - 1][j - 1] - set.size());}}return res;} }作者:灵茶山艾府 链接:https://leetcode.cn/problems/difference-of-number-of-distinct-values-on-diagonals/solutions/2287367/cong-o503-dao-o502-by-endlesscheng-5wp4/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 复杂度
- 时间复杂度: O ( m n ) \mathcal{O}(mn) O(mn)
- 空间复杂度: O ( m i n ( m , n ) ) \mathcal{O}(min(m, n)) O(min(m,n))
- 复杂度
使所有字符相等的最小成本【LC2712】
给你一个下标从 0 开始、长度为
n
的二进制字符串s
,你可以对其执行两种操作:
- 选中一个下标
i
并且反转从下标0
到下标i
(包括下标0
和下标i
)的所有字符,成本为i + 1
。- 选中一个下标
i
并且反转从下标i
到下标n - 1
(包括下标i
和下标n - 1
)的所有字符,成本为n - i
。返回使字符串内所有字符 相等 需要的 最小成本 。
反转 字符意味着:如果原来的值是 ‘0’ ,则反转后值变为 ‘1’ ,反之亦然。
-
思路:贪心(万万没想到)
从左到右遍历字符串,如果 s [ i − 1 ] ≠ s [ i ] s[i-1] \ne s[i] s[i−1]=s[i],那么必须反转,有两种操作可以选择,选择成本较小的操作进行反转
- 反转下标
0
到下标i-1
的所有字符,成本为i
->反转后对后续字符没有影响 - 反转下标
i
到下标n-1
的所有字符,成本为n-i
->反转后后续字符全部进行了反转,与之前的字符串是等价的,对最终成本没有影响
- 反转下标
-
实现
class Solution {public long minimumCost(String s) {long res = 0L;int n = s.length();for (int i = 1; i < n; i++){if (s.charAt(i) != s.charAt(i - 1)){res += Math.min(i, n - i);}}return res;} } 作者:灵茶山艾府 链接:https://leetcode.cn/problems/minimum-cost-to-make-all-characters-equal/solutions/2286922/yi-ci-bian-li-jian-ji-xie-fa-pythonjavac-aut0/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
- 复杂度
矩阵中严格递增的单元格数【LC2713】
给你一个下标从 1 开始、大小为
m x n
的整数矩阵mat
,你可以选择任一单元格作为 起始单元格 。从起始单元格出发,你可以移动到 同一行或同一列 中的任何其他单元格,但前提是目标单元格的值 严格大于 当前单元格的值。
你可以多次重复这一过程,从一个单元格移动到另一个单元格,直到无法再进行任何移动。
请你找出从某个单元开始访问矩阵所能访问的 单元格的最大数量 。
返回一个表示可访问单元格最大数量的整数。
-
思路
-
贪心:由于对于每一个位置,你可以移动到 同一行或同一列 中的比它大的其他单元格。为了访问更多的单元格,我们每次应该按增量的最小值方向移动,即从小到大移动。
- 我们需要记录每个值对应的所有坐标,然后按照值从小到大的顺序访问时每个位置,因此可以使用TreeMap记录,key为值,value为所有位置,类型为
List<int[]>
- 我们需要记录每个值对应的所有坐标,然后按照值从小到大的顺序访问时每个位置,因此可以使用TreeMap记录,key为值,value为所有位置,类型为
-
dp:而对于某一个位置,我们不需要关心他是从哪一行哪一列转移而来,我们只需要知道该行以及该列移动的单元格的最大数量。因此可以定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为到达 m a t [ i ] [ j ] mat[i][j] mat[i][j]时所访问的单元格的最大数量,那么答案就是 m a x ( d p [ i ] [ j ] ) max(dp[i][j]) max(dp[i][j])。
-
实现时,需要记录每行每列移动的单元格的最大数量。相当于取某行或者某列
dp
值的最大值,因此使用数组rowMax
和colMax
数组维护,于是状态转移方程有
d p [ i ] [ j ] = m a x ( r o w M a x [ i ] , c o l M a x [ j ] ) + 1 dp[i][j]=max(rowMax[i],colMax[j])+1 dp[i][j]=max(rowMax[i],colMax[j])+1 -
遍历时,根据TreeMap,对于每个值,更新对应位置访问的数量、行列的最大值以及结果【直接更新,不需要使用数组记录】
-
-
-
实现
class Solution {public int maxIncreasingCells(int[][] mat) {var g = new TreeMap<Integer, List<int[]>>();int m = mat.length, n = mat[0].length;for (int i = 0; i < m; i++)for (int j = 0; j < n; j++)// 相同元素放在同一组,统计位置g.computeIfAbsent(mat[i][j], k -> new ArrayList<>()).add(new int[]{i, j});int ans = 0;int[] rowMax = new int[m], colMax = new int[n];for (var pos : g.values()) {var mx = new int[pos.size()]; // 先把最大值算出来,再更新 rowMax 和 colMaxfor (int i = 0; i < pos.size(); i++) {mx[i] = Math.max(rowMax[pos.get(i)[0]], colMax[pos.get(i)[1]]) + 1;ans = Math.max(ans, mx[i]);}for (int k = 0; k < pos.size(); k++) {int i = pos.get(k)[0], j = pos.get(k)[1];rowMax[i] = Math.max(rowMax[i], mx[k]); // 更新第 i 行的最大 f 值colMax[j] = Math.max(colMax[j], mx[k]); // 更新第 j 列的最大 f 值}}return ans;} }作者:灵茶山艾府 链接:https://leetcode.cn/problems/maximum-strictly-increasing-cells-in-a-matrix/solutions/2286920/dong-tai-gui-hua-you-hua-pythonjavacgo-b-axv0/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 复杂度
- 时间复杂度: O ( m n log ( m n ) ) \mathcal{O}(mn\log(mn)) O(mnlog(mn)),构建TreeMap的时间复杂度为 O ( m n log ( m n ) ) \mathcal{O}(mn\log(mn)) O(mnlog(mn))
- 空间复杂度: O ( m n ) \mathcal{O}(mn) O(mn)
- 复杂度