前言

开完班会,回去正好碰上团队搬新楼,只剩下40分钟可以打比赛了。。。差不多够本菜鸡做完前两道题。(给再多时间后面的题我也做不出😅)最终果然只做了前两道。

第一题:重新排列单词间的空格

题解一:模拟

模拟即可,注意考虑单词数为1的边界情况。

  1. class Solution {
  2. public String reorderSpaces(String text) {
  3. String[] words = text.trim().split(" +");
  4. int spaceCount = 0;
  5. for (char i : text.toCharArray()) {
  6. if (i == ' ') {
  7. ++spaceCount;
  8. }
  9. }
  10. int n;
  11. if (words.length == 1) {
  12. n = 1;
  13. } else {
  14. n = words.length - 1;
  15. }
  16. int avgSpace = spaceCount / n;
  17. StringBuilder spacePart = new StringBuilder();
  18. for (int i = 0; i < avgSpace; ++i) {
  19. spacePart.append(' ');
  20. }
  21. StringBuilder ans = new StringBuilder();
  22. for (int i = 0; i < n; ++i) {
  23. ans.append(words[i]);
  24. ans.append(spacePart);
  25. }
  26. if (words.length >= 2) {
  27. ans.append(words[n]);
  28. }
  29. for (int i = 0; i < spaceCount % n; ++i) {
  30. ans.append(' ');
  31. }
  32. return ans.toString();
  33. }
  34. }

第二题:拆分字符串使唯一子字符串的数目最大

题解一:

用一个集合来存储已经拆分得到的子字符串。递归拆分,对于还没有出现在集合中的子字符串,有拆分加入集合和继续增加字符两种决策;对于已经出现过的子字符串,直接继续增加字符串。

  1. import java.util.HashSet;
  2. import java.util.Set;
  3. class Solution {
  4. private int max;
  5. Set<String> set;
  6. public int maxUniqueSplit(String s) {
  7. set = new HashSet<>();
  8. spilt(s, 0);
  9. return max;
  10. }
  11. private void spilt(String s, int begin) {
  12. if (begin >= s.length()) {
  13. max = Math.max(max, set.size());
  14. return;
  15. }
  16. for (int i = begin + 1; i <= s.length(); ++i) {
  17. String subString = s.substring(begin, i);
  18. // 这个子字符串还没有
  19. if (!set.contains(subString)) {
  20. set.add(subString);
  21. spilt(s, i);
  22. set.remove(subString);
  23. }
  24. }
  25. }
  26. }

第三题:矩阵的最大非负积

题解一:DFS+剪枝

比赛时第一眼看到就感觉是DP,但是我DP练得还太少了,没有马上想出怎么去建模。做这题的时候只剩10分钟了,看了下数据范围感觉直接DFS暴搜也行。可惜一开始没加对于0的剪枝,最后几个点没过,赛后加上剪枝处理就过了。
注意取余操作再取得最大值返回结果时再进行,中间结果用 long 来存储。

  1. class Solution {
  2. private long max;
  3. private int row;
  4. private int col;
  5. private final int C = 1000000007;
  6. public int maxProductPath(int[][] grid) {
  7. row = grid.length - 1;
  8. col = grid[0].length - 1;
  9. max = -1;
  10. find(grid, 0, 0, grid[0][0]);
  11. return (int) (max % C);
  12. }
  13. private void find(int[][] map, int x, int y, long ans) {
  14. if ((x == row) && (y == col)) {
  15. max = Math.max(max, ans);
  16. return;
  17. }
  18. if (ans == 0) {
  19. max = Math.max(max, ans);
  20. return;
  21. }
  22. if (x + 1 <= row) {
  23. find(map, x + 1, y, ans * map[x + 1][y]);
  24. }
  25. if (y + 1 <= col) {
  26. find(map, x, y + 1, ans * map[x][y + 1]);
  27. }
  28. }
  29. }

题解二:动态规划

此题算是剑指 Offer 47. 礼物的最大价值的变式。由于在任何一步都有可能出现正负转换,两个负数相乘很有可能得到一个最大的正数,因此对于状态的存储应该包括到达每一格时的最大值和最小值,在动态转移方程上有一定的改变。
第 207 场周赛回顾 - 图1

第 207 场周赛回顾 - 图2

最终右下角那格的最大值只需要从 dpMax 中取得即可。

  1. class Solution {
  2. private final int C = 1000000007;
  3. public int maxProductPath(int[][] grid) {
  4. final int row = grid.length;
  5. final int col = grid[0].length;
  6. // dpMax[i][j]表示到达(i, j)时的最大值
  7. long[][] dpMax = new long[row][col];
  8. // dpMin[i][j]表示到达(i, j)时的最小值
  9. long[][] dpMin = new long[row][col];
  10. // 边界情况初始化
  11. dpMax[0][0] = dpMin[0][0] = grid[0][0];
  12. for (int i = 1; i < row; ++i) {
  13. dpMax[i][0] = dpMin[i][0] = dpMax[i - 1][0] * grid[i][0];
  14. }
  15. for (int i = 1; i < col; ++i) {
  16. dpMax[0][i] = dpMin[0][i] = dpMax[0][i - 1] * grid[0][i];
  17. }
  18. long[] temp = new long[4];
  19. for (int i = 1; i < row; ++i) {
  20. for (int j = 1; j < col; ++j) {
  21. temp[0] = dpMax[i - 1][j] * grid[i][j];
  22. temp[1] = dpMin[i - 1][j] * grid[i][j];
  23. temp[2] = dpMax[i][j - 1] * grid[i][j];
  24. temp[3] = dpMin[i][j - 1] * grid[i][j];
  25. dpMax[i][j] = Long.MIN_VALUE;
  26. dpMin[i][j] = Long.MAX_VALUE;
  27. for (long t : temp) {
  28. dpMax[i][j] = Math.max(dpMax[i][j], t);
  29. dpMin[i][j] = Math.min(dpMin[i][j], t);
  30. }
  31. }
  32. }
  33. return Math.max((int) (dpMax[row - 1][col - 1] % C), -1);
  34. }
  35. }

第四题:

题解一:

思路