【LeetCode每日一题合集】2023.8.21-2023.8.27(统计点对的数目)

2337. 移动片段得到字符串⭐

https://leetcode.cn/problems/move-pieces-to-obtain-a-string/

在这里插入图片描述
提示:
n == start.length == target.length
1 <= n <= 10^5
start 和 target 由字符 'L'、'R' 和 '_' 组成

解法——脑筋急转弯

https://leetcode.cn/problems/move-pieces-to-obtain-a-string/solutions/1658923/nao-jin-ji-zhuan-wan-pythonjavacgo-by-en-9sqt/
在这里插入图片描述

class Solution {
    public boolean canChange(String start, String target) {
        int n = start.length();
        for (int i = 0, j = 0; i < n || j < n; ++i, ++j) {
            while (i < n && start.charAt(i) == '_') ++i;
            while (j < n && target.charAt(j) == '_') ++j;

            if (i < n && j < n) {
                if (start.charAt(i) != target.charAt(j)) return false;		// 不相等
                else if (start.charAt(i) == 'L' && i < j) return false;		// L不能往右移动
                else if (start.charAt(i) == 'R' && i > j) return false;		// R不能往左移动
            } else if (i < n || j < n) return false;						// 没匹配完
        }
        return true;
    }
}

849. 到最近的人的最大距离

https://leetcode.cn/problems/maximize-distance-to-closest-person/
在这里插入图片描述
提示:
2 <= seats.length <= 2 * 10^4
seats[i] 为 0 或 1
至少有一个 空座位
至少有一个 座位上有人

分为三种情况:

  1. 坐在两个人之间
  2. 坐在最右侧
  3. 坐在最左侧
class Solution {
    public int maxDistToClosest(int[] seats) {
        int n = seats.length, ans = 0, last = -1;   // last记录上一个有人的位置
        for (int i = 0; i < n; ++i) {
            if (seats[i] == 1) {
                if (last != -1) ans = Math.max(ans, (i - last) / 2);    // 求d
                else ans = Math.max(ans, i);        // 出现了第一个有人的位置
                last = i;
            }
        }
        return Math.max(ans, n - 1 - last);         // 计算放在最后一个位置时的距离
    }
}

1782. 统计点对的数目🚹🚹🚹🚹🚹

https://leetcode.cn/problems/count-pairs-of-nodes/

在这里插入图片描述
提示:
2 <= n <= 2 * 10^4
1 <= edges.length <= 10^5
1 <= ui, vi <= n
ui != vi
1 <= queries.length <= 20
0 <= queries[j] < edges.length

解法——从双指针到终极优化

https://leetcode.cn/problems/count-pairs-of-nodes/solutions/2400682/ji-bai-100cong-shuang-zhi-zhen-dao-zhong-yhze/

单独处理每个询问

int[] deg 存一下与每个点相连的边的数量。
HashMap<> cntE存一下每种边出现的次数。

参考 167. 两数之和 II - 输入有序数组 的思路,将 deg 排序,然后双指针计算满足的点对数。
最后检查 cntE 中多被计算的边数,减去之后就是 ans[j]。

在这里插入图片描述
最后这句 deg[x] + deg[y] - c <= queries[j] 的原因是,x 和 y 之间有 c 条边,这 c 条边都贡献在了 deg[x] 和 deg[y] 之中,所以就多算了 c 次。
把多算的 c 次去掉之后,如果不满足了,那这个点对(x,y)也就不是满足要求的了。将 ans[j] 减 1。

class Solution {
    public int[] countPairs(int n, int[][] edges, int[] queries) {
        int[] deg = new int[n + 1];     // 表示与 i 相连的边的数目
        Map<Integer, Integer> cntE = new HashMap<>();   // 记录每种边出现的次数
        for (int[] e: edges) {
            int x = e[0], y = e[1];
            deg[x]++;
            deg[y]++;

            // 用一个int存储两个不超过 65535 的数
            if (x > y) {        // 让 x 是更小的数,y 是更大的数
                int t = x;
                x = y;
                y = t;
            }
            cntE.merge(x << 16 | y, 1, Integer::sum);
        }

        int m = queries.length;
        int[] ans = new int[m];
        int[] sortedDeg = deg.clone();
        Arrays.sort(sortedDeg);
        for (int j = 0; j < m; ++j) {
            int q = queries[j];
            int l = 1, r = n;
            // 使用双指针计算ans[j]
            while (l < r) {
                if (sortedDeg[l] + sortedDeg[r] <= q) {
                    // 不满足要求,右移左指针
                    l++;
                } else {
                    // 从l+1,...,r都可以和l配对,左移右指针
                    ans[j] += r - l;
                    r--;
                }
            }
            // 去掉多计算的点对
            for (Map.Entry<Integer, Integer> e: cntE.entrySet()) {
                int k = e.getKey(), c = e.getValue();
                int s = deg[k >> 16] + deg[k & 0xffff];     // 取出k的高16位和低16位
                if (s > q && s - c <= q) {
                    // 这是多计算的点对,将 ans[j] 减一
                    ans[j]--;
                }
            }
        }
        return ans;
    }
}

终极优化TODO

脑子不够用了,咱先不优化。

在这里插入代码片

技巧总结

用一个int存储两个不超过 65535 的数

存储

cntE.merge(x << 16 | y, 1, Integer::sum);

取出

int s = deg[k >> 16] + deg[k & 0xffff];     // 取出k的高16位和低16位

1267. 统计参与通信的服务器

https://leetcode.cn/problems/count-servers-that-communicate/
在这里插入图片描述
提示:
m == grid.length
n == grid[i].length
1 <= m <= 250
1 <= n <= 250
grid[i][j] == 0 or 1

解法——两次循环+计数数组

class Solution {
    public int countServers(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int ans = 0;
        int[] row = new int[m], col = new int[n];
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) {
                    row[i]++;
                    col[j]++;
                }
            }
        }
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1 && (row[i] > 1 || col[j] > 1)) ++ans;
            }
        }
        return ans;
    }
}

1448. 统计二叉树中好节点的数目

https://leetcode.cn/problems/count-good-nodes-in-binary-tree/

在这里插入图片描述
提示:
二叉树中节点数目范围是 [1, 10^5] 。
每个节点权值的范围是 [-10^4, 10^4] 。

代码1

class Solution {
    int ans = 0;

    public int goodNodes(TreeNode root) {
        dfs(root, Integer.MIN_VALUE);
        return ans;
    }

    public void dfs(TreeNode root, int mx) {
        if (root == null) return;
        if (root.val >= mx) {
            ++ans;
            mx = root.val;
        }
        dfs(root.left, mx);
        dfs(root.right, mx);
    }
}

代码2

class Solution {
    public int goodNodes(TreeNode root) {
        return dfs(root, Integer.MIN_VALUE);
    }

    public int dfs(TreeNode root, int mx) {
        int res = 0;
        if (root == null) return res;
        if (root.val >= mx) {
            ++res;
            mx = root.val;
        }
        res += dfs(root.left, mx) + dfs(root.right, mx);
        return res;
    }
}

228. 汇总区间

https://leetcode.cn/problems/summary-ranges/description/
在这里插入图片描述
提示:
0 <= nums.length <= 20
-2^31 <= nums[i] <= 2^31 - 1
nums 中的所有值都 互不相同
nums 按升序排列

分组循环,一次遍历

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> ans = new ArrayList<>();
        for (int i = 0; i < nums.length; ++i) {
            int j = i;
            while (j + 1 < nums.length && nums[j + 1] == nums[j] + 1) j++;
            if (j == i) ans.add(Integer.toString(nums[i]));
            else ans.add(nums[i] + "->" + nums[j]);
            i = j;
        }
        return ans;
    }
}

分组循环相关题目列表(🐂模板)

题目列表来自:https://leetcode.cn/problems/summary-ranges/solutions/553645/hui-zong-qu-jian-by-leetcode-solution-6zrs/comments/2106748

一般来说,分组循环的模板如下(根据题目调整):

i, n = 0, len(nums)
while i < n:
    start = i
    while i < n and ...:
        i += 1
    # 从 start 到 i-1 是一段
    # 下一段从 i 开始,无需 i+=1

也就是每次记录一下 start,继续用 while 枚举 i。

1446. 连续字符

https://leetcode.cn/problems/consecutive-characters/
在这里插入图片描述

提示:
1 <= s.length <= 500
s 只包含小写英文字母。

class Solution {
    public int maxPower(String s) {
        int ans = 0;
        for (int i = 0; i < s.length(); ++i) {
            int start = i;
            while (i + 1 < s.length() && s.charAt(i + 1) == s.charAt(i)) i++;
            ans = Math.max(ans, i - start + 1);
        }
        return ans;
    }
}

1869. 哪种连续子字符串更长

https://leetcode.cn/problems/longer-contiguous-segments-of-ones-than-zeros/
在这里插入图片描述
提示:
1 <= s.length <= 100
s[i] 不是 '0' 就是 '1'

class Solution {
    public boolean checkZeroOnes(String s) {
        int l0 = 0, l1 = 0, n = s.length();
        for (int i = 0; i < n; ++i) {
            int start = i;
            while (i + 1 < n && s.charAt(i) == s.charAt(i + 1)) ++i;
            if (s.charAt(start) == '0') l0 = Math.max(l0, i - start + 1);
            if (s.charAt(start) == '1') l1 = Math.max(l1, i - start + 1);
        }
        return l1 > l0;
    }
}

1957. 删除字符使字符串变好

https://leetcode.cn/problems/delete-characters-to-make-fancy-string/
在这里插入图片描述
提示:
1 <= s.length <= 10^5
s 只包含小写英文字母。

class Solution {
    public String makeFancyString(String s) {
        StringBuilder ans = new StringBuilder();
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            int start = i;
            while (i + 1 < n && s.charAt(i + 1) == s.charAt(i)) ++i;
            if (i - start + 1 >= 3) ans.append(s.substring(start, start + 2));
            else ans.append(s.substring(start, i + 1));
        }
        return ans.toString();
    }
}

2038. 如果相邻两个颜色均相同则删除当前颜色

https://leetcode.cn/problems/remove-colored-pieces-if-both-neighbors-are-the-same-color/

在这里插入图片描述

提示:
1 <= colors.length <= 10^5
colors 只包含字母 'A' 和 'B'

class Solution {
    public boolean winnerOfGame(String colors) {
        int a = 0, b = 0, n = colors.length();
        for (int i = 0; i < n; ++i) {
            int start = i;
            while (i + 1 < n && colors.charAt(i + 1) == colors.charAt(i)) i++;
            if (colors.charAt(start) == 'A') a += Math.max(i - start - 1, 0);
            if (colors.charAt(start) == 'B') b += Math.max(i - start - 1, 0);
        }
        return a > b;
    }
}

56. 合并区间

https://leetcode.cn/problems/merge-intervals/
在这里插入图片描述
提示:
1 <= intervals.length <= 10^4
intervals[i].length == 2
0 <= starti <= endi <= 10^4

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        List<int[]> ans = new ArrayList<>();
        for (int i = 0; i < intervals.length; ++i) {
            if (ans.size() == 0 || intervals[i][0] > ans.get(ans.size() - 1)[1]) ans.add(intervals[i]);
            else ans.get(ans.size() - 1)[1] = Math.max(intervals[i][1], ans.get(ans.size() - 1)[1]);
        }
        return ans.toArray(new int[ans.size()][2]);
    }
}

更多相关题目可见:【算法】区间合并类题目总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wei *

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值