image.png
前三题都比较简单,第四题调了很久,终于在结束后的10分钟靠自己的努力写出来了!
还好没掉分!!

5967. 检查是否所有 A 都在 B 之前

给你一个 由字符 ‘a’ 和 ‘b’ 组成的字符串 s 。如果字符串中 每个 ‘a’ 都出现在 每个 ‘b’ 之前,返回 true ;否则,返回 false 。

示例 1:
输入:s = “aaabbb” 输出:true 解释: ‘a’ 位于下标 0、1 和 2 ;而 ‘b’ 位于下标 3、4 和 5 。 因此,每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 true 。
示例 2:
输入:s = “abab” 输出:false 解释: 存在一个 ‘a’ 位于下标 2 ,而一个 ‘b’ 位于下标 1 。 因此,不能满足每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 false 。
示例 3:
输入:s = “bbb” 输出:true 解释: 不存在 ‘a’ ,因此可以视作每个 ‘a’ 都出现在每个 ‘b’ 之前,所以返回 true 。

提示:

  • 1 <= s.length <= 100
  • s[i] 为 ‘a’ 或 ‘b’

思路:
找到最后一个a和第一个b出现的位置,比较他们的大小

  1. class Solution {
  2. public boolean checkString(String s) {
  3. int n = s.length();
  4. int lasta = -1, firstb = n;
  5. for (int i = 0; i < n; i++) {
  6. char ch = s.charAt(i);
  7. if (ch == 'a') lasta = i;
  8. else firstb = Math.min(firstb, i);
  9. }
  10. return lasta < firstb;
  11. }
  12. }

5968. 银行中的激光束数量

银行内部的防盗安全装置已经激活。给你一个下标从 0 开始的二进制字符串数组 bank ,表示银行的平面图,这是一个大小为 m x n 的二维矩阵。 bank[i] 表示第 i 行的设备分布,由若干 ‘0’ 和若干 ‘1’ 组成。’0’ 表示单元格是空的,而 ‘1’ 表示单元格有一个安全设备。
对任意两个安全设备而言,如果同时 满足下面两个条件,则二者之间存在 一个 激光束:

  • 两个设备位于两个 不同行 :r1 和 r2 ,其中 r1 < r2 。
  • 满足 r1 < i < r2 的 所有 行 i ,都 没有安全设备

激光束是独立的,也就是说,一个激光束既不会干扰另一个激光束,也不会与另一个激光束合并成一束。
返回银行中激光束的总数量。

示例 1:
输入:bank = [“011001”,”000000”,”010100”,”001000”] 输出:8 解释:在下面每组设备对之间,存在一条激光束。总共是 8 条激光束:
bank[0][1] — bank[2][1]
bank[0][1] — bank[2][3]
bank[0][2] — bank[2][1]
bank[0][2] — bank[2][3]
bank[0][5] — bank[2][1]
bank[0][5] — bank[2][3]
bank[2][1] — bank[3][2]
bank[2][3] — bank[3][2] 注意,第 0 行和第 3 行上的设备之间不存在激光束。 这是因为第 2 行存在安全设备,这不满足第 2 个条件。
示例 2:
输入:bank = [“000”,”111”,”000”] 输出:0 解释:不存在两个位于不同行的设备

提示:

  • m == bank.length
  • n == bank[i].length
  • 1 <= m, n <= 500
  • bank[i][j] 为 ‘0’ 或 ‘1’

思路:
就很简单的题,遍历计算即可

  1. class Solution {
  2. public int numberOfBeams(String[] bank) {
  3. int last = 0;
  4. int res = 0;
  5. int n = bank.length, m = bank[0].length();
  6. for (int i = 0; i < n; i++) {
  7. int cnt = 0;
  8. for (int j = 0; j < m; j++) {
  9. if (bank[i].charAt(j) == '1')
  10. cnt++;
  11. }
  12. res += last * cnt;
  13. if (cnt != 0)
  14. last = cnt;
  15. }
  16. return res;
  17. }
  18. }

5969. 摧毁小行星

给你一个整数 mass ,它表示一颗行星的初始质量。再给你一个整数数组 asteroids ,其中 asteroids[i] 是第 i 颗小行星的质量。
你可以按 任意顺序 重新安排小行星的顺序,然后让行星跟它们发生碰撞。如果行星碰撞时的质量 大于等于 小行星的质量,那么小行星被 摧毁 ,并且行星会 获得 这颗小行星的质量。否则,行星将被摧毁。
如果所有小行星 能被摧毁,请返回 true ,否则返回 false 。

示例 1:
输入:mass = 10, asteroids = [3,9,19,5,21] 输出:true 解释:一种安排小行星的方式为 [9,19,5,3,21] : - 行星与质量为 9 的小行星碰撞。新的行星质量为:10 + 9 = 19 - 行星与质量为 19 的小行星碰撞。新的行星质量为:19 + 19 = 38 - 行星与质量为 5 的小行星碰撞。新的行星质量为:38 + 5 = 43 - 行星与质量为 3 的小行星碰撞。新的行星质量为:43 + 3 = 46 - 行星与质量为 21 的小行星碰撞。新的行星质量为:46 + 21 = 67 所有小行星都被摧毁。
示例 2:
输入:mass = 5, asteroids = [4,9,23,4] 输出:false 解释: 行星无论如何没法获得足够质量去摧毁质量为 23 的小行星。 行星把别的小行星摧毁后,质量为 5 + 4 + 9 + 4 = 22 。 它比 23 小,所以无法摧毁最后一颗小行星。

提示:

  • 1 <= mass <= 105
  • 1 <= asteroids.length <= 105
  • 1 <= asteroids[i] <= 105

思路:
贪心,将小行星按质量从小到大排序,依次用行星撞,能撞就加,不能撞返回false,能全部撞完返回true

  1. class Solution {
  2. public boolean asteroidsDestroyed(int mass, int[] a) {
  3. Arrays.sort(a);
  4. long x = mass;
  5. for (int v : a) {
  6. if (x >= v)
  7. x += v;
  8. else
  9. return false;
  10. }
  11. return true;
  12. }
  13. }

方法2:一种基于桶的线性做法
桶依次为[1, 2), [2, 4), [4, 8), ... [2x - 1, 2x],数据范围在10^5以内,实际只需17个即可,我开了30个,不过不影响。
将所有数加到对应桶中并记录每个桶的最小值
遍历所有桶,如果当前行星质量小于桶中最小值,说明不够撞了,返回false
否则加上这个最小值后,会大于等于该桶内的所有值。故依次添加桶内所有值即可。

  1. class Solution {
  2. public boolean asteroidsDestroyed(int mass, int[] asteroids) {
  3. List<Integer>[] list = new ArrayList[30];
  4. int[] min = new int[30];
  5. Arrays.fill(min, 200000);
  6. for (int i = 0; i < 30; i++)
  7. list[i] = new ArrayList<>();
  8. boolean flag = true;
  9. for (int x : asteroids) {
  10. int cur = x;
  11. int i;
  12. for (i = 30; i >= 0; i--)
  13. if ((1 << i & cur) != 0)
  14. break;
  15. list[i].add(x);
  16. min[i] = Math.min(min[i], x);
  17. }
  18. long w = mass;
  19. int idx = 0;
  20. for (List<Integer> ll : list) {
  21. if (ll.size() == 0) {
  22. idx++;
  23. continue;
  24. }
  25. if (w < min[idx++]) {
  26. flag = false;
  27. break;
  28. } else {
  29. for (int x : ll) {
  30. w += x;
  31. }
  32. }
  33. }
  34. return flag;
  35. }
  36. }

5970. 参加会议的最多员工数

一个公司准备组织一场会议,邀请名单上有 n 位员工。公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工。
员工编号为 0 到 n - 1 。每位员工都有一位 喜欢 的员工,每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议。每位员工喜欢的员工 不会 是他自己。
给你一个下标从 0 开始的整数数组 favorite ,其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目

示例 1:
输入:favorite = [2,2,1,2] 输出:3 解释: 上图展示了公司邀请员工 0,1 和 2 参加会议以及他们在圆桌上的座位。 没办法邀请所有员工参与会议,因为员工 2 没办法同时坐在 0,1 和 3 员工的旁边。 注意,公司也可以邀请员工 1,2 和 3 参加会议。 所以最多参加会议的员工数目为 3 。
示例 2:
输入:favorite = [1,2,0] 输出:3 解释: 每个员工都至少是另一个员工喜欢的员工。所以公司邀请他们所有人参加会议的前提是所有人都参加了会议。 座位安排同图 1 所示: - 员工 0 坐在员工 2 和 1 之间。 - 员工 1 坐在员工 0 和 2 之间。 - 员工 2 坐在员工 1 和 0 之间。 参与会议的最多员工数目为 3 。
示例 3:
输入:favorite = [3,0,1,4,1] 输出:4 解释: 上图展示了公司可以邀请员工 0,1,3 和 4 参加会议以及他们在圆桌上的座位。 员工 2 无法参加,因为他喜欢的员工 0 旁边的座位已经被占领了。 所以公司只能不邀请员工 2 。 参加会议的最多员工数目为 4 。

提示:

  • n == favorite.length
  • 2 <= n <= 105
  • 0 <= favorite[i] <= n - 1
  • favorite[i] != i

思路;
可以将所有人分组,每个组都是一个仅有一个环的树形结构
将所有相关联的组分成两大类,根据环的大小来分,分成等于2和大于2的两类
所有环等于2的组能合并到一起(每个组中能坐到一起的最大值是环能延申出的最长链包含的人数),而所有环大于2的组只能是成环的那些人能坐在一起。
故最终结果在两者中取max即可

代码实现思路:

  1. 先建反向图,如果a要靠b,就连一条边从b指向a

找到所有长为2的环,对每个环进行深搜找最长路径并累加,在深搜过程中对已经搜过的点打标记

  1. 对所有剩余节点建正向图,如果a要靠b,就连一条边从a指向b,并记录每条边的入度

将所有入度为0的点入队,依次处理每组节点
找到环并统计环长,和最终结果取max,在这个过程中将遍历过的打上标记

  1. 剩余那些没打标记的形成了几个首尾相连的环,每一组可以从任意起点处开始深搜,

找到环并统计环长,和最终结果取max,在这个过程中将遍历过的打上标记

  1. 返回最终结果即可
  1. class Solution {
  2. int N = 100010;
  3. int[] h = new int[N], e = new int[N], ne = new int[N];
  4. int n, idx;
  5. boolean[] st = new boolean[N];
  6. public int maximumInvitations(int[] a) {
  7. n = a.length;
  8. Arrays.fill(h, -1);
  9. int[] cin = new int[n];
  10. // 1
  11. List<Integer> list = new ArrayList<>();
  12. for (int i = 0; i < n; i++) {
  13. if (i == a[a[i]])
  14. list.add(i);
  15. int b = i, c = a[i];
  16. add(c, b);
  17. }
  18. int res = 0;
  19. int cc = 0;
  20. for (int x : list) {
  21. int b = x, c = a[x];
  22. if (st[b]) continue;
  23. st[b] = st[c] = true;
  24. int cnt = 0;
  25. cnt += dfs(b, a) + dfs(c, a);
  26. cc += cnt;
  27. }
  28. res = Math.max(res, cc);
  29. // 2
  30. Arrays.fill(h, -1);
  31. idx = 0;
  32. for (int i = 0; i < n; i++) {
  33. int b = i, c = a[i];
  34. add(b, c);
  35. cin[c]++;
  36. }
  37. Queue<Integer> q = new LinkedList<>();
  38. for (int i = 0; i < n; i++) {
  39. if (!st[i] && cin[i] == 0)
  40. q.offer(i);
  41. }
  42. while (!q.isEmpty()) {
  43. int cur = q.poll();
  44. int slow = cur, fast = cur;
  45. int cnt = 0;
  46. do {
  47. slow = a[slow];
  48. st[slow] = true;
  49. fast = a[a[fast]];
  50. } while (slow != fast);
  51. slow = cur;
  52. while (slow != fast) {
  53. slow = a[slow];
  54. fast = a[fast];
  55. st[fast] = true;
  56. }
  57. do {
  58. fast = a[fast];
  59. cnt++;
  60. } while (fast != slow);
  61. res = Math.max(cnt, res);
  62. }
  63. for (int i = 0; i < n; i++) {
  64. if (!st[i])
  65. res = Math.max(dfs2(i, a), res);
  66. }
  67. // 3
  68. for (int i = 0; i < n; i++) {
  69. if (!st[i])
  70. res = Math.max(res, dfs2(i, a));
  71. }
  72. // 4
  73. return res;
  74. }
  75. int dfs2(int u, int[] a) {
  76. st[u] = true;
  77. int res = 1;
  78. if (!st[a[u]])
  79. res += dfs(a[u], a);
  80. return res;
  81. }
  82. void add(int a, int b) {
  83. e[idx] = b;
  84. ne[idx] = h[a];
  85. h[a] = idx++;
  86. }
  87. int dfs(int u, int[] a) {
  88. int cnt = 1;
  89. int max = 0;
  90. for (int i = h[u]; i != -1; i = ne[i]) {
  91. int j = e[i];
  92. if (!st[j]) {
  93. st[j] = true;
  94. max = Math.max(max, dfs(j, a));
  95. }
  96. }
  97. return cnt + max;
  98. }
  99. }