image.png
保持良好的心态,虽然经历了昨晚的失利,但稳住就会有奇迹发生,今早又刷新了最好名次,是因为题简单吗(我觉得还好吧,而且没Wrong,这是最令人激动的)

6070. 计算字符串的数字和

给你一个由若干数字(0 - 9)组成的字符串 s ,和一个整数。
如果 s 的长度大于 k ,则可以执行一轮操作。在一轮操作中,需要完成以下工作:

  1. 将 s 拆分 成长度为 k 的若干 连续数字组 ,使得前 k 个字符都分在第一组,接下来的 k 个字符都分在第二组,依此类推。注意,最后一个数字组的长度可以小于 k 。
  2. 用表示每个数字组中所有数字之和的字符串来 替换 对应的数字组。例如,”346” 会替换为 “13” ,因为 3 + 4 + 6 = 13 。
  3. 合并 所有组以形成一个新字符串。如果新字符串的长度大于 k 则重复第一步。

返回在完成所有轮操作后的 s 。

示例 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. class Solution {
  2. public String digitSum(String s, int k) {
  3. if (s.length() <= k)
  4. return s;
  5. int n = s.length();
  6. StringBuilder sb = new StringBuilder();
  7. for (int i = 0; i < s.length(); i++) {
  8. int j = Math.min(n, i + k);
  9. String t = s.substring(i, j);
  10. sb.append(deal(t));
  11. i = j - 1;
  12. }
  13. return digitSum(sb.toString(), k);
  14. }
  15. int deal(String s) {
  16. int x = 0;
  17. for (int i = 0; i < s.length(); i++)
  18. x += s.charAt(i) - '0';
  19. return x;
  20. }
  21. }

6071. 完成所有任务需要的最少轮数

给你一个下标从 0 开始的整数数组 tasks ,其中 tasks[i] 表示任务的难度级别。在每一轮中,你可以完成 2 个或者 3 个 相同难度级别 的任务。
返回完成所有任务需要的 最少 轮数,如果无法完成所有任务,返回 -1

示例 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 <= 105
  • 1 <= tasks[i] <= 109

思路:
贪心 + 分类讨论
能分3个一组就分3个一组,但有一点,只能分成每组2个或3个任务,而且所有任务都必须分出去
如果一个级别只有1个任务,无法分组
如果c % 3 == 0全部分成3个一组
如果c % 3 == 1最后4个任务两两一组,其它3个一组
如果c % 3 == 2最后2个任务两两一组,其它3个一组

  1. class Solution {
  2. public int minimumRounds(int[] tasks) {
  3. int cnt = 0;
  4. Arrays.sort(tasks);
  5. int n = tasks.length;
  6. for (int i = 0; i < n; i++) {
  7. int j = i;
  8. while (j < n && tasks[j] == tasks[i])
  9. j++;
  10. int c = j - i;
  11. if (c == 1) return -1;
  12. if (c % 3 == 1) {
  13. cnt += 2;
  14. c -= 4;
  15. cnt += c / 3;
  16. } else if (c % 3 == 0) {
  17. cnt += c / 3;
  18. } else if (c % 3 == 2) {
  19. cnt += 1;
  20. c -= 2;
  21. cnt += c / 3;
  22. }
  23. i = j - 1;
  24. }
  25. return cnt;
  26. }
  27. }

6072. 转角路径的乘积中最多能有几个尾随零

给你一个二维整数数组 grid ,大小为 m x n,其中每个单元格都含一个正整数。
转角路径 定义为:包含至多一个弯的一组相邻单元。具体而言,路径应该完全 向水平方向 或者 向竖直方向 移动过弯(如果存在弯),而不能访问之前访问过的单元格。在过弯之后,路径应当完全朝 另一个 方向行进:如果之前是向水平方向,那么就应该变为向竖直方向;反之亦然。当然,同样不能访问之前已经访问过的单元格。
一条路径的 乘积 定义为:路径上所有值的乘积。
请你从 grid 中找出一条乘积中尾随零数目最多的转角路径,并返回该路径中尾随零的数目。
注意:

  • 水平 移动是指向左或右移动。
  • 竖直 移动是指向上或下移动。

示例 1:
image.png
输入: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:
image.png
输入:grid = [[4,3,2],[7,6,1],[8,8,8]] 输出:0 解释:网格如上图所示。 不存在乘积含尾随零的转角路径。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 105
  • 1 <= m * n <= 105
  • 1 <= grid[i][j] <= 1000

思路:
前缀和 + 数学
分解每个元素,统计2和5的因子数,后缀0只能由2 * 5得来
对于每个元素统计上下左右4个方向2和5的个数的前缀和
找最大的即可

时间复杂度:O(nm)
空间复杂度:O(nm)

  1. class Solution {
  2. int n, m;
  3. int N = 100010;
  4. int[][] v2;
  5. int[][] v5;
  6. int[][][] up, down, left, right;
  7. public int maxTrailingZeros(int[][] g) {
  8. n = g.length;
  9. m = g[0].length;
  10. v2 = new int[n][m];
  11. v5 = new int[n][m];
  12. up = new int[n + 2][m + 2][2];
  13. down = new int[n + 2][m + 2][2];
  14. left = new int[n + 2][m + 2][2];
  15. right = new int[n + 2][m + 2][2];
  16. for (int i = 0; i < n; i++) {
  17. for (int j = 0; j < m; j++) {
  18. int x = g[i][j];
  19. int c2 = 0, c5 = 0;
  20. while (x % 2 == 0) {
  21. c2++;
  22. x /= 2;
  23. }
  24. while (x % 5 == 0) {
  25. c5++;
  26. x /= 5;
  27. }
  28. v2[i][j] = c2;
  29. v5[i][j] = c5;
  30. }
  31. }
  32. for (int i = 1; i <= n; i++) {
  33. for (int j = 1; j <= m; j++) {
  34. left[i][j][0] += left[i][j - 1][0] + v2[i - 1][j - 1];
  35. left[i][j][1] += left[i][j - 1][1] + v5[i - 1][j - 1];
  36. }
  37. for (int j = m; j >= 1; j--) {
  38. right[i][j][0] += right[i][j + 1][0] + v2[i - 1][j - 1];
  39. right[i][j][1] += right[i][j + 1][1] + v5[i - 1][j - 1];
  40. }
  41. }
  42. for (int i = 1; i <= m; i++) {
  43. for (int j = 1; j <= n; j++) {
  44. up[j][i][0] += up[j - 1][i][0] + v2[j - 1][i - 1];
  45. up[j][i][1] += up[j - 1][i][1] + v5[j - 1][i - 1];
  46. }
  47. for (int j = n; j >= 1; j--) {
  48. down[j][i][0] += down[j + 1][i][0] + v2[j - 1][i - 1];
  49. down[j][i][1] += down[j + 1][i][1] + v5[j - 1][i - 1];
  50. }
  51. }
  52. int max = 0;
  53. for (int i = 1; i <= n; i++) {
  54. for (int j = 1; j <= m; j++) {
  55. max = Math.max(deal(up[i][j], left[i][j], v2[i - 1][j - 1], v5[i - 1][j - 1]), max);
  56. max = Math.max(deal(up[i][j], right[i][j], v2[i - 1][j - 1], v5[i - 1][j - 1]), max);
  57. max = Math.max(deal(down[i][j], left[i][j], v2[i - 1][j - 1], v5[i - 1][j - 1]), max);
  58. max = Math.max(deal(down[i][j], right[i][j], v2[i - 1][j - 1], v5[i - 1][j - 1]), max);
  59. }
  60. }
  61. return max;
  62. }
  63. int deal(int[] a, int[] b, int c2, int c5) {
  64. int x2 = a[0] + b[0] - c2;
  65. int x5 = a[1] + b[1] - c5;
  66. return Math.min(x2, x5);
  67. }
  68. }

6073. 相邻字符不同的最长路径

给你一棵 (即一个连通、无向、无环图),根节点是节点 0 ,这棵树由编号从 0 到 n - 1 的 n 个节点组成。用下标从 0 开始、长度为 n 的数组 parent 来表示这棵树,其中 parent[i] 是节点 i 的父节点,由于节点 0 是根节点,所以 parent[0] == -1 。
另给你一个字符串 s ,长度也是 n ,其中 s[i] 表示分配给节点 i 的字符。
请你找出路径上任意一对相邻节点都没有分配到相同字符的 最长路径 ,并返回该路径的长度。

示例 1:
image.png
输入:parent = [-1,0,0,1,1,2], s = “abacbe” 输出:3 解释:任意一对相邻节点字符都不同的最长路径是:0 -> 1 -> 3 。该路径的长度是 3 ,所以返回 3 。 可以证明不存在满足上述条件且比 3 更长的路径。
示例 2:
image.png
输入:parent = [-1,0,0,0], s = “aabc” 输出:3 解释:任意一对相邻节点字符都不同的最长路径是:2 -> 0 -> 3 。该路径的长度为 3 ,所以返回 3 。

提示:

  • n == parent.length == s.length
  • 1 <= n <= 105
  • 对所有 i >= 1 ,0 <= parent[i] <= n - 1 均成立
  • parent[0] == -1
  • parent 表示一棵有效的树
  • s 仅由小写英文字母组成

思路:
经典树形DP找最长路径

  1. class Solution {
  2. int N = 100010;
  3. int[] h = new int[N], e = new int[N], ne = new int[N];
  4. int idx = 0;
  5. int n;
  6. char[] ch;
  7. int max = 0;
  8. // int[] w = new int[N];
  9. public int longestPath(int[] parent, String s) {
  10. this.n = parent.length;
  11. ch = s.toCharArray();
  12. Arrays.fill(h, -1);
  13. for (int i = 1; i < n; i++) {
  14. int a = parent[i], b = i;
  15. add(a, b);
  16. }
  17. dfs(0);
  18. return max + 1;
  19. }
  20. int dfs(int u) {
  21. int d1 = 0, d2 = 0;
  22. for (int i = h[u]; i != -1; i = ne[i]) {
  23. int j = e[i];
  24. int d = dfs(j) + 1;
  25. if (ch[j] == ch[u]) continue;
  26. if (d >= d1) {
  27. d2 = d1;
  28. d1 = d;
  29. } else if (d >= d2) {
  30. d2 = d;
  31. }
  32. }
  33. max = Math.max(max, d1 + d2);
  34. return d1;
  35. }
  36. void add(int a, int b) {
  37. e[idx] = b;
  38. ne[idx] = h[a];
  39. h[a] = idx++;
  40. }
  41. }