image.png
t3发现是倒着来的后改了,但没完全改,导致wrong了一发
t4自己压根没想清楚
"ab", "cd", "abc", "bcd"应该是在一个连通块中,但是按照我的处理方法,不可能将"ab", "cd"和在一起。。
别畏惧数据规模,不超1e8就大胆尝试!!!

5993. 将找到的值乘以 2

给你一个整数数组 nums ,另给你一个整数 original ,这是需要在 nums 中搜索的第一个数字。
接下来,你需要按下述步骤操作:

  1. 如果在 nums 中找到 original ,将 original 乘以 2 ,得到新 original(即,令 original = 2 * original)。
  2. 否则,停止这一过程。
  3. 只要能在数组中找到新 original ,就对新 original 继续 重复 这一过程

返回_ _original 的 最终 值。

示例 1:
输入:nums = [5,3,6,1,12], original = 3 输出:24 解释: - 3 能在 nums 中找到。3 2 = 6 。 - 6 能在 nums 中找到。6 2 = 12 。 - 12 能在 nums 中找到。12 2 = 24 。 - 24 不能在 nums 中找到。因此,返回 24 。
示例 2:
输入:nums = [2,7,9], original = 4 输出:4 *解释:
- 4 不能在 nums 中找到。因此,返回 4 。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i], original <= 1000

思路:
用哈希表即可
时间复杂度O(n)

  1. class Solution {
  2. public int findFinalValue(int[] nums, int o) {
  3. Set<Integer> set = new HashSet<>();
  4. for (int x : nums)
  5. set.add(x);
  6. while (set.contains(o))
  7. o *= 2;
  8. return o;
  9. }
  10. }

5981. 分组得分最高的所有下标

给你一个下标从 0 开始的二进制数组 nums ,数组长度为 n 。nums 可以按下标 i( 0 <= i <= n )拆分成两个数组(可能为空):numsleft 和 numsright 。

  • numsleft 包含 nums 中从下标 0 到 i - 1 的所有元素(包括 0i - 1,而 numsright 包含 nums 中从下标 i 到 n - 1 的所有元素(包括 in - 1 )。
  • 如果 i == 0 ,numsleft 为 ,而 numsright 将包含 nums 中的所有元素。
  • 如果 i == n ,numsleft 将包含 nums 中的所有元素,而 numsright 为

下标 i 分组得分 为 numsleft 中 0 的个数和 numsright 中 1 的个数之
返回 分组得分 最高所有不同下标 。你可以按 任意顺序 返回答案。

示例 1:
输入:nums = [0,0,1,0] 输出:[2,4] 解释:按下标分组 - 0 :numsleft 为 [] 。numsright 为 [0,0,1,0] 。得分为 0 + 1 = 1 。 - 1 :numsleft 为 [0] 。numsright 为 [0,1,0] 。得分为 1 + 1 = 2 。 - 2 :numsleft 为 [0,0] 。numsright 为 [1,0] 。得分为 2 + 1 = 3 。 - 3 :numsleft 为 [0,0,1] 。numsright 为 [0] 。得分为 2 + 0 = 2 。 - 4 :numsleft 为 [0,0,1,0] 。numsright 为 [] 。得分为 3 + 0 = 3 。 下标 2 和 4 都可以得到最高的分组得分 3 。 注意,答案 [4,2] 也被视为正确答案。
示例 2:
输入:nums = [0,0,0] 输出:[3] 解释:按下标分组 - 0 :numsleft 为 [] 。numsright 为 [0,0,0] 。得分为 0 + 0 = 0 。 - 1 :numsleft 为 [0] 。numsright 为 [0,0] 。得分为 1 + 0 = 1 。 - 2 :numsleft 为 [0,0] 。numsright 为 [0] 。得分为 2 + 0 = 2 。 - 3 :numsleft 为 [0,0,0] 。numsright 为 [] 。得分为 3 + 0 = 3 。 只有下标 3 可以得到最高的分组得分 3 。
示例 3:
输入:nums = [1,1] 输出:[0] 解释:按下标分组 - 0 :numsleft 为 [] 。numsright 为 [1,1] 。得分为 0 + 2 = 2 。 - 1 :numsleft 为 [1] 。numsright 为 [1] 。得分为 0 + 1 = 1 。 - 2 :numsleft 为 [1,1] 。numsright 为 [] 。得分为 0 + 0 = 0 。 只有下标 0 可以得到最高的分组得分 2 。

提示:

  • n == nums.length
  • 1 <= n <= 105
  • nums[i] 为 0 或 1

思路:
前缀和
就是边界处理好恶心啊,还不如左右都多开点,更好处理!

  1. class Solution {
  2. public List<Integer> maxScoreIndices(int[] nums) {
  3. int n = nums.length;
  4. int[] pre = new int[n + 2];
  5. for (int i = 1; i <= n; i++) {
  6. pre[i] += pre[i - 1] + nums[i - 1];
  7. }
  8. pre[n + 1] = pre[n];
  9. List<Integer> res = new ArrayList<>();
  10. int max = 0;
  11. for (int i = 1; i <= n + 1; i++) {
  12. int l = i - pre[i - 1], r = pre[n] - pre[i - 1];
  13. int sum = l + r;
  14. if (sum == max) {
  15. res.add(i - 1);
  16. } else if (sum > max) {
  17. res.clear();
  18. max = sum;
  19. res.add(i - 1);
  20. }
  21. }
  22. return res;
  23. }
  24. }

5994. 查找给定哈希值的子串

给定整数 p 和 m ,一个长度为 k 且下标从 0 开始的字符串 s 的哈希值按照如下函数计算:

  • hash(s, p, m) = (val(s[0]) p0 + val(s[1]) p1 + … + val(s[k-1]) * pk-1) mod m.

其中 val(s[i]) 表示 s[i] 在字母表中的下标,从 val(‘a’) = 1 到 val(‘z’) = 26 。
给你一个字符串 s 和整数 power,modulo,k 和 hashValue 。请你返回 s 中 第一个 长度为 k 的 子串 sub ,满足_ _hash(sub, power, modulo) == hashValue 。
测试数据保证一定 存在 至少一个这样的子串。
子串 定义为一个字符串中连续非空字符组成的序列。

示例 1:
输入:s = “leetcode”, power = 7, modulo = 20, k = 2, hashValue = 0 输出:“ee” 解释:“ee” 的哈希值为 hash(“ee”, 7, 20) = (5 1 + 5 7) mod 20 = 40 mod 20 = 0 。 “ee” 是长度为 2 的第一个哈希值为 0 的子串,所以我们返回 “ee” 。
示例 2:
输入:s = “fbxzaad”, power = 31, modulo = 100, k = 3, hashValue = 32 输出:“fbx” 解释:“fbx” 的哈希值为 hash(“fbx”, 31, 100) = (6 1 + 2 31 + 24 312) mod 100 = 23132 mod 100 = 32 。 “bxz” 的哈希值为 hash(“bxz”, 31, 100) = (2 1 + 24 31 + 26 312) mod 100 = 25732 mod 100 = 32 。 “fbx” 是长度为 3 的第一个哈希值为 32 的子串,所以我们返回 “fbx” 。 注意,”bxz” 的哈希值也为 32 ,但是它在字符串中比 “fbx” 更晚出现。

提示:

  • 1 <= k <= s.length <= 2 * 104
  • 1 <= power, modulo <= 109
  • 0 <= hashValue < modulo
  • s 只包含小写英文字母。
  • 测试数据保证一定 存在 满足条件的子串。

思路:
注意读题,本题的字符串哈希是从后往前的

  1. class Solution {
  2. public String subStrHash(String s, int p, int m, int k, int hashValue) {
  3. int n = s.length();
  4. long pow = 1;
  5. for (int i = 1; i <= k; i++)
  6. pow = (pow * p) % m;
  7. long[] pre = new long[n + 2];
  8. for (int i = n - 1; i >= 0; i--) {
  9. pre[i] = (pre[i + 1] * p + s.charAt(i) - 'a' + 1) % m;
  10. }
  11. for (int i = 0; i <= n - k; i++) {
  12. long hash = ((pre[i] - pre[i + k] * pow) % m + m) % m;
  13. if (hash == hashValue)
  14. return s.substring(i, i + k);
  15. }
  16. return null;
  17. }
  18. }

5995. 字符串分组

给你一个下标从 0 开始的字符串数组 words 。每个字符串都只包含 小写英文字母 。words 中任意一个子串中,每个字母都至多只出现一次。
如果通过以下操作之一,我们可以从 s1 的字母集合得到 s2 的字母集合,那么我们称这两个字符串为 关联的

  • 往 s1 的字母集合中添加一个字母。
  • 从 s1 的字母集合中删去一个字母。
  • 将 s1 中的一个字母替换成另外任意一个字母(也可以替换为这个字母本身)。

数组 words 可以分为一个或者多个无交集的 。一个字符串与一个组如果满足以下 任一 条件,它就属于这个组:

  • 它与组内 至少 一个其他字符串关联。
  • 它是这个组中 唯一 的字符串。

注意,你需要确保分好组后,一个组内的任一字符串与其他组的字符串都不关联。可以证明在这个条件下,分组方案是唯一的。
请你返回一个长度为 2 的数组 ans :

  • ans[0] 是 words 分组后的 总组数
  • ans[1] 是字符串数目最多的组所包含的字符串数目。

示例 1:
输入:words = [“a”,”b”,”ab”,”cde”] 输出:[2,3] 解释: - words[0] 可以得到 words[1] (将 ‘a’ 替换为 ‘b’)和 words[2] (添加 ‘b’)。所以 words[0] 与 words[1] 和 words[2] 关联。 - words[1] 可以得到 words[0] (将 ‘b’ 替换为 ‘a’)和 words[2] (添加 ‘a’)。所以 words[1] 与 words[0] 和 words[2] 关联。 - words[2] 可以得到 words[0] (删去 ‘b’)和 words[1] (删去 ‘a’)。所以 words[2] 与 words[0] 和 words[1] 关联。 - words[3] 与 words 中其他字符串都不关联。 所以,words 可以分成 2 个组 [“a”,”b”,”ab”] 和 [“cde”] 。最大的组大小为 3 。
示例 2:
输入:words = [“a”,”ab”,”abc”] 输出:[1,3] 解释: - words[0] 与 words[1] 关联。 - words[1] 与 words[0] 和 words[2] 关联。 - words[2] 与 words[1] 关联。 由于所有字符串与其他字符串都关联,所以它们全部在同一个组内。 所以最大的组大小为 3 。

提示:

  • 1 <= words.length <= 2 * 104
  • 1 <= words[i].length <= 26
  • words[i] 只包含小写英文字母。
  • words[i] 中每个字母最多只出现一次。

思路:
并查集 + 位运算
时间复杂度:O(26 26 N)
空间复杂度:O(N)

  1. class Solution {
  2. public int[] groupStrings(String[] words) {
  3. int n = words.length;
  4. int[] a = new int[n];
  5. var uf = new UnionFind();
  6. for (int i = 0; i < n; i++) {
  7. int x = 0;
  8. for (int j = 0; j < words[i].length(); j++) {
  9. x |= 1 << (words[i].charAt(j) - 'a');
  10. }
  11. a[i] = x;
  12. }
  13. for (int x : a) {
  14. if (uf.add(x)) {
  15. for (int i = 0; i < 26; i++) {
  16. if ((x >> i & 1) == 1) {
  17. int v = x ^ (1 << i);
  18. for (int j = 0; j < 26; j++) {
  19. if (i != j && (v >> j & 1) == 0)
  20. uf.merge(x, v ^ (1 << j));
  21. }
  22. }
  23. }
  24. for (int i = 0; i < 26; i++)
  25. uf.merge(x, x ^ (1 << i));
  26. }
  27. }
  28. int max = 0, cnt = 0;
  29. Map<Integer, Integer> map = new HashMap<>();
  30. for (int x : a) {
  31. map.merge(uf.find(x), 1, Integer::sum);
  32. max = Math.max(max, map.get(uf.find(x)));
  33. }
  34. return new int[]{map.size(), max};
  35. }
  36. }
  37. class UnionFind {
  38. Map<Integer, Integer> fa = new HashMap<>();
  39. boolean add(int x) {
  40. if (fa.get(x) == null) {
  41. fa.put(x, x);
  42. return true;
  43. } else
  44. return false;
  45. }
  46. void merge(int a, int b) {
  47. if (fa.get(a) == null || fa.get(b) == null)
  48. return;
  49. int pa = find(a), pb = find(b);
  50. if (pa != pb) {
  51. fa.put(pa, pb);
  52. }
  53. }
  54. int find(int x) {
  55. if (fa.get(x) == x)
  56. return x;
  57. else {
  58. int p = find(fa.get(x));
  59. fa.put(x, p);
  60. return p;
  61. }
  62. }
  63. }