image.png
掉大分的一场。

6124. 第一个出现两次的字母

给你一个由小写英文字母组成的字符串 s ,请你找出并返回第一个出现 两次 的字母。
注意:

  • 如果 a 的 第二次 出现比 b 的 第二次 出现在字符串中的位置更靠前,则认为字母 a 在字母 b 之前出现两次。
  • s 包含至少一个出现两次的字母。

示例 1:
输入:s = “abccbaacz” 输出:“c” 解释: 字母 ‘a’ 在下标 0 、5 和 6 处出现。 字母 ‘b’ 在下标 1 和 4 处出现。 字母 ‘c’ 在下标 2 、3 和 7 处出现。 字母 ‘z’ 在下标 8 处出现。 字母 ‘c’ 是第一个出现两次的字母,因为在所有字母中,’c’ 第二次出现的下标是最小的。
示例 2:
输入:s = “abcdd” 输出:“d” 解释: 只有字母 ‘d’ 出现两次,所以返回 ‘d’ 。

提示:

  • 2 <= s.length <= 100
  • s 由小写英文字母组成
  • s 包含至少一个重复字母

思路:
模拟

  1. class Solution {
  2. public char repeatedCharacter(String s) {
  3. int[] c = new int[26];
  4. for (int i = 0; i < s.length(); i++) {
  5. c[s.charAt(i) - 'a']++;
  6. if (c[s.charAt(i) - 'a'] > 1) return s.charAt(i);
  7. }
  8. return ' ';
  9. }
  10. }
  1. class Solution {
  2. public char repeatedCharacter(String s) {
  3. Set<Character> set = new HashSet<>();
  4. for (int i = 0; i < s.length(); i++) {
  5. if (!set.add(s.charAt(i)))
  6. return s.charAt(i);
  7. }
  8. return ' ';
  9. }
  10. }

6125. 相等行列对

给你一个下标从 0 开始、大小为 n x n 的整数矩阵 grid ,返回满足 Ri 行和 _Cj 列相等的行列对 (Ri, Cj) 的数目。_
如果行和列以相同的顺序包含相同的元素(即相等的数组),则认为二者是相等的。

示例 1:
image.png
输入:grid = [[3,2,1],[1,7,6],[2,7,7]] 输出:1 解释:存在一对相等行列对: - (第 2 行,第 1 列):[2,7,7]
示例 2:
image.png
输入:grid = [[3,1,2,2],[1,4,4,5],[2,4,2,2],[2,4,2,2]] 输出:3 解释:存在三对相等行列对: - (第 0 行,第 0 列):[3,1,2,2] - (第 2 行, 第 2 列):[2,4,2,2] - (第 3 行, 第 2 列):[2,4,2,2]

提示:

  • n == grid.length == grid[i].length
  • 1 <= n <= 200
  • 1 <= grid[i][j] <= 105

思路:
n的范围很小,直接暴力处理
时间复杂度:O(n3)

  1. class Solution {
  2. public int equalPairs(int[][] g) {
  3. int n = g.length;
  4. int c = 0;
  5. // row
  6. for (int i = 0; i < n; i++) {
  7. // col
  8. for (int j = 0; j < n; j++) {
  9. boolean f = true;
  10. for (int k = 0; k < n; k++) {
  11. if (g[i][k] != g[k][j]) {
  12. f = false;
  13. break;
  14. }
  15. }
  16. if (f) c++;
  17. }
  18. }
  19. return c;
  20. }
  21. }

6126. 设计食物评分系统

设计一个支持下述操作的食物评分系统:

  • 修改 系统中列出的某种食物的评分。
  • 返回系统中某一类烹饪方式下评分最高的食物。

实现 FoodRatings 类:

  • FoodRatings(String[] foods, String[] cuisines, int[] ratings) 初始化系统。食物由 foods、cuisines 和 ratings 描述,长度均为 n 。
    • foods[i] 是第 i 种食物的名字。
    • cuisines[i] 是第 i 种食物的烹饪方式。
    • ratings[i] 是第 i 种食物的最初评分。
  • void changeRating(String food, int newRating) 修改名字为 food 的食物的评分。
  • String highestRated(String cuisine) 返回指定烹饪方式 cuisine 下评分最高的食物的名字。如果存在并列,返回 字典序较小 的名字。

注意,字符串 x 的字典序比字符串 y 更小的前提是:x 在字典中出现的位置在 y 之前,也就是说,要么 x 是 y 的前缀,或者在满足 x[i] != y[i] 的第一个位置 i 处,x[i] 在字母表中出现的位置在 y[i] 之前。

示例:
输入 [“FoodRatings”, “highestRated”, “highestRated”, “changeRating”, “highestRated”, “changeRating”, “highestRated”] [[[“kimchi”, “miso”, “sushi”, “moussaka”, “ramen”, “bulgogi”], [“korean”, “japanese”, “japanese”, “greek”, “japanese”, “korean”], [9, 12, 8, 15, 14, 7]], [“korean”], [“japanese”], [“sushi”, 16], [“japanese”], [“ramen”, 16], [“japanese”]] 输出 [null, “kimchi”, “ramen”, null, “sushi”, null, “ramen”] 解释 FoodRatings foodRatings = new FoodRatings([“kimchi”, “miso”, “sushi”, “moussaka”, “ramen”, “bulgogi”], [“korean”, “japanese”, “japanese”, “greek”, “japanese”, “korean”], [9, 12, 8, 15, 14, 7]); foodRatings.highestRated(“korean”); // 返回 “kimchi” // “kimchi” 是分数最高的韩式料理,评分为 9 。 foodRatings.highestRated(“japanese”); // 返回 “ramen” // “ramen” 是分数最高的日式料理,评分为 14 。 foodRatings.changeRating(“sushi”, 16); // “sushi” 现在评分变更为 16 。 foodRatings.highestRated(“japanese”); // 返回 “sushi” // “sushi” 是分数最高的日式料理,评分为 16 。 foodRatings.changeRating(“ramen”, 16); // “ramen” 现在评分变更为 16 。 foodRatings.highestRated(“japanese”); // 返回 “ramen” // “sushi” 和 “ramen” 的评分都是 16 。 // 但是,”ramen” 的字典序比 “sushi” 更小。

提示:

  • 1 <= n <= 2 * 104
  • n == foods.length == cuisines.length == ratings.length
  • 1 <= foods[i].length, cuisines[i].length <= 10
  • foods[i]、cuisines[i] 由小写英文字母组成
  • 1 <= ratings[i] <= 108
  • foods 中的所有字符串 互不相同
  • 在对 changeRating 的所有调用中,food 是系统中食物的名字。
  • 在对 highestRated 的所有调用中,cuisine 是系统中 至少一种 食物的烹饪方式。
  • 最多调用 changeRating 和 highestRated 总计 2 * 104 次

思路:
方法1:Map + TreeMap + TreeSet
究极套娃

  1. class FoodRatings {
  2. class Node {
  3. String c;
  4. int v;
  5. Node(String c, int v) {
  6. this.c = c;
  7. this.v = v;
  8. }
  9. }
  10. // 食物-{做法,分数}
  11. Map<String, Node> map = new HashMap<>();
  12. // 做法-(分数-食物集和)
  13. Map<String, TreeMap<Integer, TreeSet<String>>> m2 = new HashMap<>();
  14. public FoodRatings(String[] f, String[] c, int[] r) {
  15. for (int i = 0; i < f.length; i++) {
  16. map.put(f[i], new Node(c[i], r[i]));
  17. if (!m2.containsKey(c[i]))
  18. m2.put(c[i], new TreeMap<>());
  19. TreeMap<Integer, TreeSet<String>> t = m2.get(c[i]);
  20. if (!t.containsKey(r[i]))
  21. t.put(r[i], new TreeSet<>());
  22. t.get(r[i]).add(f[i]);
  23. }
  24. }
  25. public void changeRating(String food, int newRating) {
  26. if (!map.containsKey(food)) return;
  27. Node old = map.get(food);
  28. m2.get(old.c).get(old.v).remove(food);
  29. if (m2.get(old.c).get(old.v).size() == 0)
  30. m2.get(old.c).remove(old.v);
  31. if (!m2.get(old.c).containsKey(newRating))
  32. m2.get(old.c).put(newRating, new TreeSet<>());
  33. m2.get(old.c).get(newRating).add(food);
  34. old.v = newRating;
  35. }
  36. public String highestRated(String c) {
  37. Integer x = m2.get(c).lastKey();
  38. return m2.get(c).get(x).first();
  39. }
  40. }
  41. /**
  42. * Your FoodRatings object will be instantiated and called as such:
  43. * FoodRatings obj = new FoodRatings(foods, cuisines, ratings);
  44. * obj.changeRating(food,newRating);
  45. * String param_2 = obj.highestRated(cuisine);
  46. */
  1. // 利用Java 9新特性 Map.entry() 生成 Map.Entry<key, value>
  2. class FoodRatings {
  3. // 食物-{做法,分数}
  4. Map<String, Map.Entry<String, Integer>> map = new HashMap<>();
  5. // 做法-食物集
  6. Map<String, TreeSet<String>> m2 = new HashMap<>();
  7. public FoodRatings(String[] f, String[] c, int[] r) {
  8. for (int i = 0; i < f.length; i++) {
  9. map.put(f[i], Map.entry(c[i], r[i]));
  10. m2.computeIfAbsent(c[i], key -> new TreeSet<>((o1, o2) -> {
  11. return map.get(o1).getValue().equals(map.get(o2).getValue()) ? o1.compareTo(o2)
  12. : map.get(o2).getValue() - map.get(o1).getValue();
  13. })).add(f[i]);
  14. }
  15. }
  16. public void changeRating(String food, int newRating) {
  17. String c = map.get(food).getKey();
  18. m2.get(c).remove(food);
  19. map.put(food, Map.entry(c, newRating));
  20. m2.get(c).add(food);
  21. }
  22. public String highestRated(String cuisine) {
  23. return m2.get(cuisine).first();
  24. }
  25. }
  26. /**
  27. * Your FoodRatings object will be instantiated and called as such:
  28. * FoodRatings obj = new FoodRatings(foods, cuisines, ratings);
  29. * obj.changeRating(food,newRating);
  30. * String param_2 = obj.highestRated(cuisine);
  31. */

方法2:Map + 优先队列

  1. class FoodRatings {
  2. class Food {
  3. String f, s;
  4. int v;
  5. Food(String f, String s, int v) {
  6. this.f = f;
  7. this.s = s;
  8. this.v = v;
  9. }
  10. }
  11. Map<String, Map.Entry<String, Integer>> map = new HashMap<>();
  12. Map<String, PriorityQueue<Food>> m2 = new HashMap<>();
  13. public FoodRatings(String[] f, String[] c, int[] r) {
  14. for (int i = 0; i < f.length; i++) {
  15. map.put(f[i], Map.entry(c[i], r[i]));
  16. }
  17. for (int i = 0; i < f.length; i++) {
  18. m2.computeIfAbsent(c[i], key -> new PriorityQueue<>((o1, o2) -> {
  19. return o1.v == o2.v ? o1.f.compareTo(o2.f) : o2.v - o1.v;
  20. })).add(new Food(f[i], c[i], r[i]));
  21. }
  22. }
  23. public void changeRating(String food, int newRating) {
  24. String c = map.get(food).getKey();
  25. map.put(food, Map.entry(c, newRating));
  26. m2.get(c).add(new Food(food, c, newRating));
  27. }
  28. public String highestRated(String cuisine) {
  29. var t = m2.get(cuisine);
  30. while (!t.isEmpty()) {
  31. var a = t.peek();
  32. if (map.get(a.f).getValue() != a.v) {
  33. t.poll();
  34. continue;
  35. } else {
  36. return a.f;
  37. }
  38. }
  39. return null;
  40. }
  41. }
  42. /**
  43. * Your FoodRatings object will be instantiated and called as such:
  44. * FoodRatings obj = new FoodRatings(foods, cuisines, ratings);
  45. * obj.changeRating(food,newRating);
  46. * String param_2 = obj.highestRated(cuisine);
  47. */

6127. 优质数对的数目

给你一个下标从 0 开始的正整数数组 nums 和一个正整数 k 。
如果满足下述条件,则数对 (num1, num2) 是 优质数对

  • num1 和 num2 在数组 nums 中存在。
  • num1 OR num2 和 num1 AND num2 的二进制表示中值为 1 的位数之和大于等于 k ,其中 OR 是按位 操作,而 AND 是按位 操作。

返回 不同 优质数对的数目。
如果 a != c 或者 b != d ,则认为 (a, b) 和 (c, d) 是不同的两个数对。例如,(1, 2) 和 (2, 1) 不同。
注意:如果 num1 在数组中至少出现 一次 ,则满足 num1 == num2 的数对 (num1, num2) 也可以是优质数对。

示例 1:
输入:nums = [1,2,3,1], k = 3 输出:5 解释:有如下几个优质数对: - (3, 3):(3 AND 3) 和 (3 OR 3) 的二进制表示都等于 (11) 。值为 1 的位数和等于 2 + 2 = 4 ,大于等于 k = 3 。 - (2, 3) 和 (3, 2): (2 AND 3) 的二进制表示等于 (10) ,(2 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。 - (1, 3) 和 (3, 1): (1 AND 3) 的二进制表示等于 (01) ,(1 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。 所以优质数对的数目是 5 。
示例 2:
输入:nums = [5,1,1], k = 10 输出:0 解释:该数组中不存在优质数对。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 1 <= k <= 60

思路:
方法1:统计+ 二分

  1. class Solution {
  2. public long countExcellentPairs(int[] nums, int k) {
  3. Set<Integer> set = new HashSet<>();
  4. for (int x : nums) set.add(x);
  5. long c = 0;
  6. int[][] a = new int[set.size()][2];
  7. int idx = 0;
  8. for (int x : set) {
  9. a[idx][0] = x;
  10. a[idx][1] = Integer.bitCount(x);
  11. int len = a[idx++][1];
  12. if (len * 2 >= k)
  13. c++;
  14. }
  15. Arrays.sort(a, (o1, o2) -> (o1[1] - o2[1]));
  16. for (int i = 0; i < a.length; i++) {
  17. if (a[i][1] >= k) {
  18. c += (a.length - i - 1) * 2L;
  19. continue;
  20. }
  21. int l = i + 1, r = a.length - 1, target = k - a[i][1];
  22. if (a[a.length - 1][1] < target) continue;
  23. while (l < r) {
  24. int mid = l + r >> 1;
  25. if (a[mid][1] >= target)
  26. r = mid;
  27. else
  28. l = mid + 1;
  29. }
  30. c += (a.length - l) * 2L;
  31. }
  32. return c;
  33. }
  34. }
  1. 方法2 按位数统计不同的数的个数,总共只有30种不同的位数,二分换成暴力搜索
  1. class Solution {
  2. public long countExcellentPairs(int[] nums, int k) {
  3. long res = 0;
  4. int[] c = new int[31];
  5. Set<Integer> set = new HashSet<>();
  6. for (int x : nums) {
  7. c[Integer.bitCount(x)] += set.add(x) ? 1 : 0;
  8. }
  9. for (int i = 0; i < 31; i++) {
  10. for (int j = Math.max(0, k - i); j < 31; j++) {
  11. res += 1L * c[i] * c[j];
  12. }
  13. }
  14. return res;
  15. }
  16. }