[Leetcode Weekly Contest]310

链接:LeetCode

[Leetcode]2404. 出现最频繁的偶数元素

给你一个整数数组 nums ,返回出现最频繁的偶数元素。
如果存在多个满足条件的元素,只需要返回 最小 的一个。如果不存在这样的元素,返回 -1 。

哈希表,遍历即可。

class Solution {
    public int mostFrequentEven(int[] nums) {
        HashMap<Integer, Integer> counter = new HashMap<>();
        for(var num: nums) {
            if((num & 1) == 0) {
                counter.put(num, counter.getOrDefault(num, 0) + 1);
            }
        }
        int res = -1, mx = 0;
        for(var entry:counter.entrySet()) {
            if(entry.getValue() > mx || (entry.getValue() == mx && entry.getKey() < res)) {
                res = entry.getKey();
                mx = entry.getValue();
            }
        }
        return res;
    }
}

[Leetcode]2405. 子字符串的最优划分

给你一个字符串 s ,请你将该字符串划分成一个或多个 子字符串 ,并满足每个子字符串中的字符都是 唯一 的。也就是说,在单个子字符串中,字母的出现次数都不超过 一次 。
满足题目要求的情况下,返回 最少 需要划分多少个子字符串。
注意,划分后,原字符串中的每个字符都应该恰好属于一个子字符串。

哈希表+贪心。

class Solution {
    public int partitionString(String s) {
        int res = 1;
        HashSet<Character> set = new HashSet<>();
        for(int i=0;i<s.length();++i) {
            char ch = s.charAt(i);
            if(set.contains(ch)) {
                res ++;
                set.clear();
            }
            set.add(ch);
        }
        return res;
    }
}

[Leetcode]2406. 将区间分为最少组数

给你一个二维整数数组 intervals ,其中 intervals[i] = [lefti, righti] 表示 闭 区间 [lefti, righti] 。
你需要将 intervals 划分为一个或者多个区间 组 ,每个区间 只 属于一个组,且同一个组中任意两个区间 不相交 。
请你返回 最少 需要划分成多少个组。
如果两个区间覆盖的范围有重叠(即至少有一个公共数字),那么我们称这两个区间是 相交 的。比方说区间 [1, 5] 和 [5, 8] 相交。

贪心,按照 \(\textit{left}\) 排序后,用最小堆模拟,堆顶存储每个组最后一个区间的 \(\textit{right}\)
遍历区间:

  • 如果当前的 \(\textit{left}\) 大于堆顶,则可以接在这个组的末尾,更新堆顶为 \(\textit{right}\)
  • 否则需要创建一个新的组。

另外一种思路是把区间内的数都加一,最后答案为所有数的最大值。
这可以用差分数组实现,下面代码用的平衡树,方便从小到大计算。

class Solution {
    public int minGroups(int[][] intervals) {
        Arrays.sort(intervals, (n1,n2) -> n1[0] - n2[0]);
        var pq = new PriorityQueue<Integer>();
        for(var interval:intervals) {
            if(pq.isEmpty() || interval[0] <= pq.peek()) {
                pq.offer(interval[1]);
            } else {
                pq.poll();
                pq.offer(interval[1]);
            }
        }
        return pq.size();
    }
}
// TreeMap
class Solution {
    public int minGroups(int[][] intervals) {
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for(var interval:intervals) {
            int start = interval[0], end = interval[1];
            map.put(start, map.getOrDefault(start, 0) + 1);
            map.put(end+1, map.getOrDefault(end+1, 0) - 1);
        }
        int res = 0, cur = 0;
        for(int value: map.values()) {
            cur += value;
            res = Math.max(res, cur);
        }
        return res;
    }
}

[Leetcode] 2407. 最长递增子序列 II

给你一个整数数组 nums 和一个整数 k 。
找到 nums 中满足以下要求的最长子序列:

  • 子序列 严格递增
  • 子序列中相邻元素的差值 不超过 k 。

请你返回满足上述要求的 最长子序列 的长度。
子序列 是从一个数组中删除部分元素后,剩余元素不改变顺序得到的数组。

在求解「上升子序列」问题时,一般有两种优化方法:

  • 单调栈 + 二分优化;
  • 线段树、平衡树等数据结构优化。

这里可以通过DP+线段树解决。设 \(f[i]\) 表示以数字 \(i\) 为结尾的最长递增子序列。我们按顺序枚举 nums 中的数字,那么有转移方程:

\[f[nums[i]] = max(f[nums[i]], f[j] + 1) // j >= nums[i] - k and j < nums[i] \]

也就是说我们需要一个数据结构支持求区间内的最大值以及单点更新最大值。

class Solution {
    public int lengthOfLIS(int[] nums, int k) {
        int res = 1, n = nums.length;
        int[] dp = new int[n];
        var tree = new SegmentTree();
        for(int i=0;i<n;++i) {
            int pre = tree.query(nums[i]-k, nums[i]-1);
            dp[i] = pre + 1;
            tree.modify(nums[i], pre+1);
            res = Math.max(dp[i], res);
        }
        return res;
    }
}

class SegmentTree {
    class Node {
        int left ,right;
        Node leftNode, rightNode;
        int value ;
        public Node(int left, int right, int value) {
            this.left = left;
            this.right = right;
            this.value = value;
        }
    }

    Node root;
    public  SegmentTree() {
        root = new Node(0, 100001, 0);
    }

    public int query(int queryLeft, int queryRight) {
        return queryNode(queryLeft, queryRight, root);
    }

    public int queryNode(int queryLeft, int queryRight, Node node) {

        if(node.left >= queryLeft && node.right <= queryRight) return node.value;
        int mid = node.left + (node.right-node.left) / 2;
        int ll = 0, rr = 0;
        if(queryLeft <= mid) {
            if(node.leftNode == null) ll = 0;
            else ll = queryNode(queryLeft, queryRight, node.leftNode);
        }
        if(queryRight > mid) {
            if(node.rightNode == null) rr = 0;
            else rr = queryNode(queryLeft, queryRight, node.rightNode);
        }
        return Math.max(ll, rr);
    }

    public void modify(int num, int value) {
        modifyNode(num, value, root);
    }

    public void modifyNode(int num, int value, Node node) {
        node.value = Math.max(node.value, value);
        int mid = node.left + (node.right-node.left) / 2;
        if(node.left == num && node.right == num) return;
        else if(num <= mid) {
            if(node.leftNode == null) node.leftNode = new Node(node.left, mid, value);
            modifyNode(num, value, node.leftNode);
        }
        else {
            if(node.rightNode == null) node.rightNode = new Node(mid+1, node.right, value);
            modifyNode(num, value, node.rightNode);
        }
    }
}

参考:
LeetCode

posted @ 2022-09-14 19:29  Jamest  阅读(30)  评论(0编辑  收藏  举报