[Leetcode Weekly Contest]333

链接:LeetCode

[Leetcode]2570. 合并两个二维数组 - 求和法

给你两个 二维 整数数组 nums1 和 nums2.
nums1[i] = [idi, vali] 表示编号为 idi 的数字对应的值等于 vali 。
nums2[i] = [idi, vali] 表示编号为 idi 的数字对应的值等于 vali 。
每个数组都包含 互不相同 的 id ,并按 id 以 递增 顺序排列。
请你将两个数组合并为一个按 id 以递增顺序排列的数组,并符合下述条件:

  • 只有在两个数组中至少出现过一次的 id 才能包含在结果数组内。
  • 每个 id 在结果数组中 只能出现一次 ,并且其对应的值等于两个数组中该 id 所对应的值求和。如果某个数组中不存在该 id ,则认为其对应的值等于 0 。
    返回结果数组。返回的数组需要按 id 以递增顺序排列。

遍历即可。

class KeyValuePair{
    public int key;
    public int value;

    public KeyValuePair(int key, int val) {
        this.key = key;
        this.value = val;
    }

}

class Solution {
    public int[][] mergeArrays(int[][] nums1, int[][] nums2) {
        ArrayList<KeyValuePair> result = new ArrayList<>();
        int i=0, j=0;
        while(i<nums1.length || j<nums2.length) {
            int ind = 0, val = 0;
            if(i == nums1.length) {
                ind = nums2[j][0];
                val = nums2[j][1];
                ++j;
            }
            else if(j == nums2.length) {
                ind = nums1[i][0];
                val = nums1[i][1];
                ++i;
            }
            else if(nums2[j][0] < nums1[i][0]) {
                ind = nums2[j][0];
                val = nums2[j][1];
                ++j;
            }
            else if(nums1[i][0] < nums2[j][0]) {
                ind = nums1[i][0];
                val = nums1[i][1];
                ++i;
            }
            else {
                ind = nums1[i][0];
                val = nums1[i][1] + nums2[j][1];
                i++;
                j++;
            }
            if(result.size() == 0 || ind > result.get(result.size()-1).key) result.add(new KeyValuePair(ind, val));
            else result.get(result.size()-1).value += val;
        }
        int[][] res = new int[result.size()][2];
        int ind = 0;
        for(var pair: result) {
            res[ind][0] = pair.key;
            res[ind][1] = pair.value;
            ind++;
        }
        return res;
    }
}

[Leetcode]2571. 将整数减少到零需要的最少操作数

给你一个正整数 n ,你可以执行下述操作 任意 次:

  • n 加上或减去 2 的某个 幂
  • 返回使 n 等于 0 需要执行的 最少 操作数。
    如果 \(x == 2^i\) 且其中 i >= 0 ,则数字 x 是 2 的幂。

把 n 看成二进制数,那么更高位的比特1 是会受到更低位的比特 1 的加减影响的,但是,最小的比特 1 没有这个约束。
那么考虑优先消除最小的比特 1,设它对应的数字为 lowbit。
消除方法只能是加上lowbit,或者减去 lowbit。
注意,求lowbit可以通过n & -n来得到

class Solution {
    public int minOperations(int n) {
        if(n == 0) return 0;
        int lb = n & -n;
        return 1 + Math.min(minOperations(n+lb), minOperations(n-lb));
    }
}

[Leetcode]2572. 无平方子集计数

给你一个正整数数组 nums 。
如果数组 nums 的子集中的元素乘积是一个 无平方因子数 ,则认为该子集是一个 无平方 子集。
无平方因子数 是无法被除 1 之外任何平方数整除的数字。
返回数组 nums 中 无平方 且 非空 的子集数目。因为答案可能很大,返回对 \(10^9 + 7\) 取余的结果。
nums 的 非空子集 是可以由删除 nums 中一些元素(可以不删除,但不能全部删除)得到的一个数组。如果构成两个子集时选择删除的下标不同,则认为这两个子集不同。

子集状压 DP。可以枚举 mask 的补集 other 的子集 j,按照「选或不选」的思想,有如下转移:

\[f[j∣mask]+=f[j]⋅cnt[x] \]

其中 x 是 mask 对应的 NSQ,cnt[x] 为 x 在 nums 中的出现次数。

class Solution {
    private static final int[] PRIMES = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    private static final int MOD = (int) 1e9 + 7, MX = 30, N_PRIMES = PRIMES.length, M = 1 << N_PRIMES;
    private static final int[] NSQ_TO_MASK = new int[MX + 1]; // NSQ_TO_MASK[i] 为 i 对应的质数集合(用二进制表示)

    static {
        for (int i = 2; i <= MX; ++i)
            for (int j = 0; j < N_PRIMES; ++j) {
                int p = PRIMES[j];
                if (i % p == 0) {
                    if (i % (p * p) == 0) { // 有平方因子
                        NSQ_TO_MASK[i] = -1;
                        break;
                    }
                    NSQ_TO_MASK[i] |= 1 << j; // 把 j 加到集合中
                }
            }
    }

    public int squareFreeSubsets(int[] nums) {
        var f = new int[M]; // f[j] 表示恰好组成集合 j 的方案数
        f[0] = 1; // 空集的方案数为 1
        for (int x : nums) {
            int mask = NSQ_TO_MASK[x];
            if (mask >= 0) // x 是 NSQ
                for (int j = M - 1; j >= mask; --j)
                    if ((j | mask) == j)  // mask 是 j 的子集
                        f[j] = (f[j] + f[j ^ mask]) % MOD; // 不选 mask + 选 mask
        }
        var ans = 0L;
        for (int v : f) ans += v;
        return (int) ((ans - 1) % MOD); // -1 去掉空集
    }
}

[Leetcode]2573. 找出对应 LCP 矩阵的字符串

对任一由 n 个小写英文字母组成的字符串 word ,我们可以定义一个 n x n 的矩阵,并满足:
lcp[i][j] 等于子字符串 word[i,...,n-1] 和 word[j,...,n-1] 之间的最长公共前缀的长度。
给你一个 n x n 的矩阵 lcp 。返回与 lcp 对应的、按字典序最小的字符串 word 。如果不存在这样的字符串,则返回空字符串。
对于长度相同的两个字符串 a 和 b ,如果在 a 和 b 不同的第一个位置,字符串 a 的字母在字母表中出现的顺序先于 b 中的对应字母,则认为字符串 a 按字典序比字符串 b 小。例如,"aabd" 在字典上小于 "aaca" ,因为二者不同的第一位置是第三个字母,而 'b' 先于 'c' 出现。

因为要求字典序最小,我们尝试从前往后构造字符串,这样每次填入字典序尽可能小的字符,就能得到字典序最小的答案。

class Solution {
    public String findTheString(int[][] lcp) {
        int i = 0, n = lcp.length;
        var s = new char[n];
        for (char c = 'a'; c <= 'z'; ++c) {
            while (i < n && s[i] > 0) ++i;
            if (i == n) break; // 构造完毕
            for (int j = i; j < n; ++j)
                if (lcp[i][j] > 0) s[j] = c;
        }
        while (i < n) if (s[i++] == 0) return ""; // 没有构造完

        // 直接在原数组上验证
        for (i = n - 1; i >= 0; --i)
            for (int j = n - 1; j >= 0; --j) {
                int actualLCP = s[i] != s[j] ? 0 : i == n - 1 || j == n - 1 ? 1 : lcp[i + 1][j + 1] + 1;
                if (lcp[i][j] != actualLCP) return "";
            }
        return new String(s);
    }
}

参考:LeetCode

posted @ 2023-02-23 19:57  Jamest  阅读(9)  评论(0编辑  收藏  举报