image.png
虽然AK了,但t4做的有点问题,如果数据再强一点就有可能过不了!卡太极限了!

5922. 统计出现过一次的公共字符串

给你两个字符串数组 words1 和 words2 ,请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。

示例 1:
输入:words1 = [“leetcode”,”is”,”amazing”,”as”,”is”], words2 = [“amazing”,”leetcode”,”is”] 输出:2 解释: - “leetcode” 在两个数组中都恰好出现一次,计入答案。 - “amazing” 在两个数组中都恰好出现一次,计入答案。 - “is” 在两个数组中都出现过,但在 words1 中出现了 2 次,不计入答案。 - “as” 在 words1 中出现了一次,但是在 words2 中没有出现过,不计入答案。 所以,有 2 个字符串在两个数组中都恰好出现了一次。
示例 2:
输入:words1 = [“b”,”bb”,”bbb”], words2 = [“a”,”aa”,”aaa”] 输出:0 解释:没有字符串在两个数组中都恰好出现一次。
示例 3:
输入:words1 = [“a”,”ab”], words2 = [“a”,”a”,”a”,”ab”] 输出:1 解释:唯一在两个数组中都出现一次的字符串是 “ab” 。

提示:

  • 1 <= words1.length, words2.length <= 1000
  • 1 <= words1[i].length, words2[j].length <= 30
  • words1[i] 和 words2[j] 都只包含小写英文字母。

思路:
哈希表 + 遍历

  1. class Solution {
  2. public int countWords(String[] words1, String[] words2) {
  3. Map<String, Integer> m1 = new HashMap<>();
  4. Map<String, Integer> m2 = new HashMap<>();
  5. for (String s : words1) {
  6. m1.merge(s, 1, Integer::sum);
  7. }
  8. for (String s : words2) {
  9. m2.merge(s, 1, Integer::sum);
  10. }
  11. int res = 0;
  12. for (String s : m1.keySet()) {
  13. if (m1.get(s) == 1 && m2.getOrDefault(s, 0) == 1)
  14. res++;
  15. }
  16. return res;
  17. }
  18. }

5923. 从房屋收集雨水需要的最少水桶数

给你一个下标从 0 开始的字符串 street 。street 中每个字符要么是表示房屋的 ‘H’ ,要么是表示空位的 ‘.’ 。
你可以在 空位 放置水桶,从相邻的房屋收集雨水。位置在 i - 1 或者 i + 1 的水桶可以收集位置为 i 处房屋的雨水。一个水桶如果相邻两个位置都有房屋,那么它可以收集 两个 房屋的雨水。
在确保 每个 房屋旁边都 至少 有一个水桶的前提下,请你返回需要的 最少 水桶数。如果无解请返回 -1 。

示例 1:
输入:street = “H..H” 输出:2 解释: 我们可以在下标为 1 和 2 处放水桶。 “H..H” -> “HBBH”(’B’ 表示放置水桶)。 下标为 0 处的房屋右边有水桶,下标为 3 处的房屋左边有水桶。 所以每个房屋旁边都至少有一个水桶收集雨水。
示例 2:
输入:street = “.H.H.” 输出:1 解释: 我们可以在下标为 2 处放置一个水桶。 “.H.H.” -> “.HBH.”(’B’ 表示放置水桶)。 下标为 1 处的房屋右边有水桶,下标为 3 处的房屋左边有水桶。 所以每个房屋旁边都至少有一个水桶收集雨水。
示例 3:
输入:street = “.HHH.” 输出:-1 解释: 没有空位可以放置水桶收集下标为 2 处的雨水。 所以没有办法收集所有房屋的雨水。
示例 4:
输入:street = “H” 输出:-1 解释: 没有空位放置水桶。 所以没有办法收集所有房屋的雨水。
示例 5:
输入:street = “.” 输出:0 解释: 没有房屋需要收集雨水。 所以需要 0 个水桶。

提示:

  • 1 <= street.length <= 105
  • street[i] 要么是 ‘H’ ,要么是 ‘.’ 。

思路:
贪心,如果一个房子后面能放桶就放后面,但是如果前面已经放了桶就跳过。

  1. class Solution {
  2. public int minimumBuckets(String street) {
  3. char[] c = street.toCharArray();
  4. int n = c.length;
  5. int res = 0;
  6. for (int i = 0; i < n; i++) {
  7. if (c[i] == 'H') {
  8. if ((i - 1 < 0 || c[i - 1] == 'H') && (i + 1 >= n || c[i + 1] == 'H'))
  9. return -1;
  10. if (i - 1 >= 0 && c[i - 1] == '0')
  11. continue;
  12. if (i + 1 < n && c[i + 1] == '.') {
  13. res++;
  14. c[i + 1] = '0';
  15. } else {
  16. c[i - 1] = '0';
  17. res++;
  18. }
  19. }
  20. }
  21. return res;
  22. }
  23. }

5924. 网格图中机器人回家的最小代价

给你一个 m x n 的网格图,其中 (0, 0) 是最左上角的格子,(m - 1, n - 1) 是最右下角的格子。给你一个整数数组 startPos ,startPos = [startrow, startcol] 表示 初始 有一个 机器人 在格子 (startrow, startcol) 处。同时给你一个整数数组 homePos ,homePos = [homerow, homecol] 表示机器人的 家 在格子 (homerow, homecol) 处。
机器人需要回家。每一步它可以往四个方向移动:上,下,左,右,同时机器人不能移出边界。每一步移动都有一定代价。再给你两个下标从 0 开始的额整数数组:长度为 m 的数组 rowCosts 和长度为 n 的数组 colCosts 。

  • 如果机器人往 上 或者往 下 移动到第 r 行 的格子,那么代价为 rowCosts[r] 。
  • 如果机器人往 左 或者往 右 移动到第 c 列 的格子,那么代价为 colCosts[c] 。

请你返回机器人回家需要的 最小总代价 。

示例 1:
image.png
输入:startPos = [1, 0], homePos = [2, 3], rowCosts = [5, 4, 3], colCosts = [8, 2, 6, 7] 输出:18 解释:一个最优路径为: 从 (1, 0) 开始 -> 往下走到 (2, 0) 。代价为 rowCosts[2] = 3 。 -> 往右走到 (2, 1) 。代价为 colCosts[1] = 2 。 -> 往右走到 (2, 2) 。代价为 colCosts[2] = 6 。 -> 往右走到 (2, 3) 。代价为 colCosts[3] = 7 。 总代价为 3 + 2 + 6 + 7 = 18
示例 2:
输入:startPos = [0, 0], homePos = [0, 0], rowCosts = [5], colCosts = [26] 输出:0 解释:机器人已经在家了,所以不需要移动。总代价为 0 。

提示:

  • m == rowCosts.length
  • n == colCosts.length
  • 1 <= m, n <= 105
  • 0 <= rowCosts[r], colCosts[c] <= 104
  • startPos.length == 2
  • homePos.length == 2
  • 0 <= startrow, homerow < m
  • 0 <= startcol, homecol < n

思路:脑筋急转弯,在不绕路的情况下,无论怎么走,结果都是定值。

  1. class Solution {
  2. public int minCost(int[] startPos, int[] homePos, int[] rowCosts, int[] colCosts) {
  3. int a = startPos[0], b = homePos[0], x = startPos[1], y = homePos[1];
  4. int res = 0;
  5. if (a < b) {
  6. for (int i = a; i < b; i++)
  7. res += rowCosts[i + 1];
  8. } else if (a > b) {
  9. for (int i = a; i > b; i--)
  10. res += rowCosts[i - 1];
  11. }
  12. if (x < y) {
  13. for (int i = x; i < y; i++)
  14. res += colCosts[i + 1];
  15. } else if (x > y) {
  16. for (int i = x; i > y; i--)
  17. res += colCosts[i - 1];
  18. }
  19. return res;
  20. }
  21. }

5925. 统计农场中肥沃金字塔的数目

有一个 矩形网格 状的农场,划分为 m 行 n 列的单元格。每个格子要么是 肥沃的 (用 1 表示),要么是 贫瘠 的(用 0 表示)。网格图以外的所有与格子都视为贫瘠的。
农场中的 金字塔 区域定义如下:

  1. 区域内格子数目 大于 1 且所有格子都是 肥沃的
  2. 金字塔 顶端 是这个金字塔 最上方 的格子。金字塔的高度是它所覆盖的行数。令 (r, c) 为金字塔的顶端且高度为 h ,那么金字塔区域内包含的任一格子 (i, j) 需满足 r <= i <= r + h - 1 c - (i - r) <= j <= c + (i - r) 。

一个 倒金字塔 类似定义如下:

  1. 区域内格子数目 大于 1 且所有格子都是 肥沃的
  2. 倒金字塔的 顶端 是这个倒金字塔 最下方 的格子。倒金字塔的高度是它所覆盖的行数。令 (r, c) 为金字塔的顶端且高度为 h ,那么金字塔区域内包含的任一格子 (i, j) 需满足 r - h + 1 <= i <= r c - (r - i) <= j <= c + (r - i) 。

下图展示了部分符合定义和不符合定义的金字塔区域。黑色区域表示肥沃的格子。
image.png
给你一个下标从 0 开始且大小为 m x n 的二进制矩阵 grid ,它表示农场,请你返回 grid 中金字塔和倒金字塔的 总数目

示例 1:
image.png image.png image.png
输入:grid = [[0,1,1,0],[1,1,1,1]] 输出:2 解释: 2 个可能的金字塔区域分别如上图蓝色和红色区域所示。 这个网格图中没有倒金字塔区域。 所以金字塔区域总数为 2 + 0 = 2 。
示例 2:
image.png image.png image.png
输入:grid = [[1,1,1],[1,1,1]] 输出:2 解释: 金字塔区域如上图蓝色区域所示,倒金字塔如上图红色区域所示。 所以金字塔区域总数目为 1 + 1 = 2 。
示例 3:
image.png
输入:grid = [[1,0,1],[0,0,0],[1,0,1]] 输出:0 解释: 网格图中没有任何金字塔或倒金字塔区域。
示例 4:
image.png image.png image.png image.png
输入:grid = [[1,1,1,1,0],[1,1,1,1,1],[1,1,1,1,1],[0,1,0,0,1]] 输出:13 解释: 有 7 个金字塔区域。上图第二和第三张图中展示了它们中的 3 个。 有 6 个倒金字塔区域。上图中最后一张图展示了它们中的 2 个。 所以金字塔区域总数目为 7 + 6 = 13.

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 1000
  • 1 <= m * n <= 105
  • grid[i][j] 要么是 0 ,要么是 1 。

思路:
比赛时的方法:
计算每一行的前缀和。O(nm)
暴力统计每个点作为塔顶的金字塔个数,一次遍历(每个点的时间复杂度为O(n))就可以求出正金字塔和倒金字塔。 O(n
m * n)
总时间复杂度正好是108,太极限了。
正解:DP
状态表示:f[i][j]表示(i, j)为塔尖的金字塔的层数x,这样以(i, j)为塔尖的可组成的金字塔个数为x - 1
状态转移:f[i][j] = Math.min(f[i + 1][j - 1], f[i + 1][j + 1]) + 1 且必须满足g[i + 1][j] == 1
从下往上遍历,这样可以求出正金字塔的个数。
逆金字塔的个数相当于把整个数组上下反转一下,DP再来一次。

  1. class Solution {
  2. int n, m;
  3. public int countPyramids(int[][] g) {
  4. n = g.length; m = g[0].length;
  5. int res = get(g);
  6. //反转数组
  7. for (int i = 0, j = n - 1; i < j; i++, j--) {
  8. for (int k = 0; k < m; k++) {
  9. int x = g[i][k];
  10. g[i][k] = g[j][k];
  11. g[j][k] = x;
  12. }
  13. }
  14. res += get(g);
  15. return res;
  16. }
  17. int get(int[][] g) {
  18. int res = 0;
  19. int[][] f = new int[n][m];
  20. for (int i = n - 1; i >= 0; i--) {
  21. for (int j = 0; j < m; j++) {
  22. if (g[i][j] == 1)
  23. f[i][j] = 1;
  24. else
  25. continue;
  26. if (i + 1 < n && g[i + 1][j] == 1) {
  27. int min = 0x3f3f3f3f;
  28. if (j - 1 >= 0 && j + 1 < m)
  29. min = Math.min(f[i + 1][j - 1], f[i + 1][j + 1]);
  30. f[i][j] += min == 0x3f3f3f3f ? 0 : min;
  31. }
  32. res += Math.max(f[i][j] - 1, 0);
  33. }
  34. }
  35. return res;
  36. }
  37. }