文章目录
- 2337. 移动片段得到字符串⭐
- 解法——脑筋急转弯
- 849. 到最近的人的最大距离
- 1782. 统计点对的数目🚹🚹🚹🚹🚹
- 解法——从双指针到终极优化
- 单独处理每个询问
- 终极优化TODO
- 技巧总结
- 用一个int存储两个不超过 65535 的数
- 1267. 统计参与通信的服务器
- 解法——两次循环+计数数组
- 1448. 统计二叉树中好节点的数目
- 代码1
- 代码2
- 228. 汇总区间
- 分组循环,一次遍历
- 分组循环相关题目列表(🐂模板)
- 1446. 连续字符
- 1869. 哪种连续子字符串更长
- 1957. 删除字符使字符串变好
- 2038. 如果相邻两个颜色均相同则删除当前颜色
- 56. 合并区间
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
至少有一个 空座位
至少有一个 座位上有人
分为三种情况:
- 坐在两个人之间
- 坐在最右侧
- 坐在最左侧
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]);
}
}
更多相关题目可见:【算法】区间合并类题目总结