本文是对 2022.04.17 周赛的回顾
#字符串 #模拟
题目描述
给你一个由若干数字(0 - 9)组成的字符串 s ,和一个整数。 如果 s 的长度大于 k ,则可以执行一轮操作。在一轮操作中,需要完成以下工作: 将 s 拆分 成长度为 k 的若干 连续数字组 ,使得前 k 个字符都分在第一组,接下来的 k 个字符都分在第二组,依此类推。注意,最后一个数字组的长度可以小于 k 。 用表示每个数字组中所有数字之和的字符串来 替换 对应的数字组。例如,”346” 会替换为 “13” ,因为 3 + 4 + 6 = 13 。 合并 所有组以形成一个新字符串。如果新字符串的长度大于 k 则重复第一步。 返回在完成所有轮操作后的 s 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 示例 1: 输入:s = "11111222223", k = 3 输出:"135" 解释: - 第一轮,将 s 分成:"111"、"112"、"222" 和 "23" 。 接着,计算每一组的数字和:1 + 1 + 1 = 3、1 + 1 + 2 = 4、2 + 2 + 2 = 6 和 2 + 3 = 5 。 这样,s 在第一轮之后变成 "3" + "4" + "6" + "5" = "3465" 。 - 第二轮,将 s 分成:"346" 和 "5" 。 接着,计算每一组的数字和:3 + 4 + 6 = 13 、5 = 5 。 这样,s 在第二轮之后变成 "13" + "5" = "135" 。 现在,s.length <= k ,所以返回 "135" 作为答案。 示例 2: 输入:s = "00000000", k = 3 输出:"000" 解释: 将 "000", "000", and "00". 接着,计算每一组的数字和:0 + 0 + 0 = 0 、0 + 0 + 0 = 0 和 0 + 0 = 0 。 s 变为 "0" + "0" + "0" = "000" ,其长度等于 k ,所以返回 "000" 。 提示: 1 <= s.length <= 100 2 <= k <= 100 s 仅由数字(0 - 9)组成。
解题思路 (1). 总体思路就是反复合并的过程,直到字符串长度小于等于 k
; (2). 需要注意的就是怎样寻找每个长度为 k
(最后一个小于等于 k
)的子串的起始索引。
解题代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func digitSum (s string , k int ) string { if len (s) <= k { return s } for len (s) > k { t := "" for i:=0 ; i<=(len (s)/k)*k; i=i+k { x := "" if i == (len (s)/k)*k { x = s[i:] } else { x = s[i:i+k] } if x == "" { break } var n int for j:=0 ; j<len (x); j++ { n += int (x[j])-'0' } t += strconv.Itoa(n) } s = t } return s }
#动态规划 #枚举
题目描述
给你一个下标从 0 开始的整数数组 tasks ,其中 tasks[i] 表示任务的难度级别。在每一轮中,你可以完成 2 个或者 3 个 相同难度级别 的任务。 返回完成所有任务需要的 最少 轮数,如果无法完成所有任务,返回 -1 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 示例 1: 输入:tasks = [2,2,3,3,2,4,4,4,4,4] 输出:4 解释:要想完成所有任务,一个可能的计划是: - 第一轮,完成难度级别为 2 的 3 个任务。 - 第二轮,完成难度级别为 3 的 2 个任务。 - 第三轮,完成难度级别为 4 的 3 个任务。 - 第四轮,完成难度级别为 4 的 2 个任务。 可以证明,无法在少于 4 轮的情况下完成所有任务,所以答案为 4 。 示例 2: 输入:tasks = [2,3,3] 输出:-1 解释:难度级别为 2 的任务只有 1 个,但每一轮执行中,只能选择完成 2 个或者 3 个相同难度级别的任务。因此,无法完成所有任务,答案为 -1 。 提示: 1 <= tasks.length <= 10^5 1 <= tasks[i] <= 10^9
解题思路 (1). 本题中并不关心具体的任务难度的差别,任务难度的级别仅仅是作为对任务的区分,相当于任务的编号; (2). 首先要计算每种任务各有多少个,并且记录下最多的某类任务个数; (3). 然后就可以使用动态规划,创建数组,数组的长度是最多的任务个数+1; (4). 然后就转变为类似跳台阶的问题; (5). 之后枚举所有任务,根据动态规划数组计算总轮数。
解题代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 func minimumRounds (tasks []int ) int { m := make (map [int ]int ) maxNum := 0 for i:=0 ; i<len (tasks); i++ { m[tasks[i]]++ maxNum = max(maxNum, m[tasks[i]]) } if maxNum < 2 { return -1 } dp := make ([]int , maxNum+1 ) dp[0 ], dp[1 ], dp[2 ] = 0 , 0 , 1 for i:=3 ; i<=maxNum; i++ { if i == 3 { dp[i] = 1 continue } if dp[i-2 ] == 0 && dp[i-3 ] == 0 { dp[i] = 0 } else if dp[i-2 ] == 0 { dp[i] = dp[i-3 ]+1 } else if dp[i-3 ] == 0 { dp[i] = dp[i-2 ]+1 } else { dp[i] = min(dp[i-2 ], dp[i-3 ])+1 } } ret := 0 for _,v := range m { if dp[v] == 0 { return -1 } ret += dp[v] } return ret } func max (a, b int ) int { if a > b { return a } return b } func min (a, b int ) int { if a < b { return a } return b }
#前缀和 #枚举
题目描述
给你一个二维整数数组 grid ,大小为 m x n,其中每个单元格都含一个正整数。 转角路径 定义为:包含至多一个弯的一组相邻单元。具体而言,路径应该完全 向水平方向 或者 向竖直方向 移动过弯(如果存在弯),而不能访问之前访问过的单元格。在过弯之后,路径应当完全朝 另一个 方向行进:如果之前是向水平方向,那么就应该变为向竖直方向;反之亦然。当然,同样不能访问之前已经访问过的单元格。 一条路径的 乘积 定义为:路径上所有值的乘积。 请你从 grid 中找出一条乘积中尾随零数目最多的转角路径,并返回该路径中尾随零的数目。
注意:
水平 移动是指向左或右移动。
竖直 移动是指向上或下移动。
1 2 3 4 5 6 7 8 9 输入:grid = [[23,17,15,3,20],[8,1,20,27,11],[9,4,6,2,21],[40,9,1,10,6],[22,7,4,5,3]] 输出:3 解释:左侧的图展示了一条有效的转角路径。 其乘积为 15 * 20 * 6 * 1 * 10 = 18000 ,共计 3 个尾随零。 可以证明在这条转角路径的乘积中尾随零数目最多。 中间的图不是一条有效的转角路径,因为它有不止一个弯。 右侧的图也不是一条有效的转角路径,因为它需要重复访问已经访问过的单元格。 示例 2:
1 2 3 4 5 6 7 8 9 10 11 12 输入:grid = [[4,3,2],[7,6,1],[8,8,8]] 输出:0 解释:网格如上图所示。 不存在乘积含尾随零的转角路径。 提示: m == grid.length n == grid[i].length 1 <= m, n <= 10^5 1 <= m * n <= 10^5 1 <= grid[i][j] <= 1000
解题思路 (1). 尾零的个数就是路径上的数的因子 2
的个数和,与因子 5
的个数之和的较小值; (2). 那么数越多越好,路径的起点和终点都应该在边界上。预处理因子的前缀和,然后枚举所有的路径:
从上往下走,枚举左拐/右拐;
从下往上走,枚举左拐/右拐; (3). 所有路径上的 $min(s_2,s_5)$ 的最大值即为答案,这里 $s_2$ 为路径上的因子 2
的个数之和,$s_5$ 为路径上的因子 5
的个数之和。
解题代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 var c25 [1001 ][2 ]int func init () { for i := 2 ; i <= 1000 ; i++ { if i%2 == 0 { c25[i][0 ] = c25[i/2 ][0 ] + 1 } if i%5 == 0 { c25[i][1 ] = c25[i/5 ][1 ] + 1 } } } func maxTrailingZeros (grid [][]int ) (ans int ) { m, n := len (grid), len (grid[0 ]) s := make ([][][2 ]int , m) for i, row := range grid { s[i] = make ([][2 ]int , n+1 ) for j, v := range row { s[i][j+1 ][0 ] = s[i][j][0 ] + c25[v][0 ] s[i][j+1 ][1 ] = s[i][j][1 ] + c25[v][1 ] } } for j := 0 ; j < n; j++ { s2, s5 := 0 , 0 for i, row := range grid { s2 += c25[row[j]][0 ] s5 += c25[row[j]][1 ] ans = max(ans, max(min(s2+s[i][j][0 ], s5+s[i][j][1 ]), min(s2+s[i][n][0 ]-s[i][j+1 ][0 ], s5+s[i][n][1 ]-s[i][j+1 ][1 ]))) } s2, s5 = 0 , 0 for i := m - 1 ; i >= 0 ; i-- { s2 += c25[grid[i][j]][0 ] s5 += c25[grid[i][j]][1 ] ans = max(ans, max(min(s2+s[i][j][0 ], s5+s[i][j][1 ]), min(s2+s[i][n][0 ]-s[i][j+1 ][0 ], s5+s[i][n][1 ]-s[i][j+1 ][1 ]))) } } return } func max (a, b int ) int { if a < b { return b }; return a }func min (a, b int ) int { if a > b { return b }; return a }