[Leetcode Weekly Contest]303

链接:LeetCode

[Leetcode]2351. 第一个出现两次的字母

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

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

模拟。

class Solution {
    public char repeatedCharacter(String s) {
        HashSet<Character> set = new HashSet<>();
        for(var ch:s.toCharArray()) {
            if(set.contains(ch)) return ch;
            else set.add(ch);
        }
        return ' ';
    }
}

[Leetcode]2352. 相等行列对

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

用哈希表统计每行出现的次数,然后遍历列,累加哈希表中列出现的次数。

class Solution {
    public int equalPairs(int[][] grid) {
        int res = 0;
        int n = grid.length, m = grid[0].length;
        HashMap<String, Integer> hash = new HashMap<>();
        for(int i = 0;i<n;++i) {
            StringBuilder sb = new StringBuilder();
            for(int j = 0;j<m;++j) {
                sb.append("#");
                sb.append(grid[i][j]);
            }
            hash.put(sb.toString(), hash.getOrDefault(sb.toString(), 0)+1);
        }

        for(int j=0;j<n;++j) {
            StringBuilder sb = new StringBuilder();
            for(int i=0;i<n;++i) {
                sb.append("#");
                sb.append(grid[i][j]);
            }
            if(hash.containsKey(sb.toString())) res += hash.getOrDefault(sb.toString(), 0);
        }
        return res;
    }
}

[Leetcode]2353. 设计食物评分系统

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

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

实现 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] 之前。

方法一:平衡树(有序集合)
我们可以用一个哈希表 \(\textit{fs}\) 记录每个食物名称对应的食物评分和烹饪方式,另一个哈希表套平衡树 \(\textit{cs}\) 记录每个烹饪方式对应的食物评分和食物名字集合。对于 changeRating 操作,先从 \(\textit{cs}[\textit{fs}[\textit{food}].\textit{cuisine}]\) 中删掉旧数据,然后将 \(\textit{newRating}\)\(\textit{food}\) 记录到 \(\textit{cs}\)\(\textit{fs}\) 中。
方法二:懒删除堆
另一种做法是用堆:

  • 对于 changeRating 操作,直接往 \(\textit{cs}\) 中记录,不做任何删除操作;
  • 对于 highestRated 操作,查看堆顶的食物评分是否等于其实际值,若不相同则意味着对应的元素已被替换成了其他值,堆顶存的是个垃圾数据,直接弹出堆顶;否则堆顶就是答案。
// TreeSet
class FoodRatings1 {
    HashMap<String, Pair<String, Integer>> foodDict = new HashMap<>();
    HashMap<String, TreeSet<Pair<String, Integer>>> cuisineDict = new HashMap<>();

    public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {
        for(int i=0;i<foods.length;++i) {
            String food = foods[i], cuisine = cuisines[i];
            int rating = ratings[i];
            foodDict.put(food, new Pair<>(cuisine, rating));
            cuisineDict.computeIfAbsent(cuisine, k -> new TreeSet<>((a, b) -> !Objects.equals(a.getValue(), b.getValue()) ? b.getValue()-a.getValue() : a.getKey().compareTo(b.getKey()))).add(new Pair<>(food, rating));
        }
    }

    public void changeRating(String food, int newRating) {
        var pair = foodDict.get(food);
        String cuisine = pair.getKey();
        int oldRating = pair.getValue();
        //foodDict.remove(pair);
        foodDict.put(food, new Pair<>(cuisine, newRating));

        var rawMap = cuisineDict.get(cuisine);
        rawMap.remove(new Pair<>(food, pair.getValue()));
        rawMap.add(new Pair<>(food, newRating));
    }

    public String highestRated(String cuisine) {
        return cuisineDict.get(cuisine).first().getKey();
    }
}

// PriorityQueue
class FoodRatings2 {
    HashMap<String, Pair<String, Integer>> foodDict = new HashMap<>();
    HashMap<String, PriorityQueue<Pair<String, Integer>>> cuisineDict = new HashMap<>();

    public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {
        for(int i=0;i<foods.length;++i) {
            String food = foods[i], cuisine = cuisines[i];
            int rating = ratings[i];
            foodDict.put(food, new Pair<>(cuisine, rating));
            cuisineDict.computeIfAbsent(cuisine, k -> new PriorityQueue<>((a, b) -> !Objects.equals(a.getValue(), b.getValue()) ? b.getValue()-a.getValue() : a.getKey().compareTo(b.getKey()))).add(new Pair<>(food, rating));
        }
    }

    public void changeRating(String food, int newRating) {
        var pair = foodDict.get(food);
        String cuisine = pair.getKey();
        int oldRating = pair.getValue();
        //foodDict.remove(pair);
        foodDict.put(food, new Pair<>(cuisine, newRating));

        var rawMap = cuisineDict.get(cuisine);
        rawMap.offer(new Pair<>(food, newRating));
    }

    // lazy delete
    public String highestRated(String cuisine) {
        var rawMap = cuisineDict.get(cuisine);
        while(!Objects.equals(rawMap.peek().getValue(), foodDict.get(rawMap.peek().getKey()).getValue())) {
            rawMap.poll();
        }
        return rawMap.peek().getKey();
    }
}

[Leetcode]2354. 优质数对的数目

给你一个下标从 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) 也可以是优质数对。

数学。
讨论二进制第 i 位在 num1 和 num2 中是否为 1 的情况:
若第 i 位的 1 只在 num1 和 num2 中出现一次,则它只会在 num1 OR num2 的结果中出现,对位数之和的贡献是 1;
若第 i 位的 1 在 num1 和 num2 中出现两次,则它会在 num1 OR num2 和 num1 AND num2 的结果中出现,对位数之和的贡献是 2。
也就是说,第 i 位在两个数里出现几次,它的贡献就是几。因此我们维护 f(x) 表示数 x 中有几个 1,题目变为:

求不同数对 (x,y) 的数量,使得 \(f(x) + f(y) \ge k\)

统计答案时,我们枚举 \(f(x)\)\(f(y)\)。记 \(g(t)\) 表示 \(f(x) = t\) 的不同 x 有几个。根据数学知识,满足 \(f(x) + f(y) \ge k\) 的数对有 \(g(f(x)) \times g(f(y))\) 对。把枚举过程中的所有答案加起来即可。复杂度 \(\mathcal{O}(n\log A + \log^2 A)\),其中 A 是数组中的最大元素。

class Solution {
    int[] bitcnt = new int[32];
    HashSet<Integer> set = new HashSet<>();
    public long countExcellentPairs(int[] nums, int k) {
        for(var num:nums) {
            int n = Integer.bitCount(num);
            if(!set.contains(num)) {
                bitcnt[n] += 1;
                set.add(num);
            }
        }
        long res = 0;
        for(int i=0;i<32;++i) {
            for(int j=0;j<32;++j) {
                if(i+j >= k && bitcnt[i]!=0 &&bitcnt[j]!=0)
                    res += bitcnt[i] * bitcnt[j];
            }
        }
        return res;
    }
}

参考:LeetCode
LeetCode

posted @ 2022-07-26 21:10  Jamest  阅读(42)  评论(0编辑  收藏  举报