跳至主要內容

力扣第 87 场双周赛

大约 3 分钟

力扣第 87 场双周赛

比赛时间 2022-09-17。本场周赛国服共 448 人 AK。

T1. 统计共同度过的日子数(3 分)

解题思路

模拟。把日期转化为一年中的第 n 天(即下标),再求交集即可。

时间复杂度:O(1)

参考代码

class Solution {
    private static final int MAX_N = 12;
    private static final int[] DAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    private int[] preSum;

    public int countDaysTogether(String arriveAlice, String leaveAlice, String arriveBob, String leaveBob) {
        // 前缀和
        preSum = new int[MAX_N + 1];
        for (int i = 0; i < MAX_N; i++) {
            preSum[i + 1] = preSum[i] + DAYS[i];
        }

        // 线段取交集
        int arriveMax = Math.max(getIdx(arriveAlice), getIdx(arriveBob));
        int leaveMin = Math.min(getIdx(leaveAlice), getIdx(leaveBob));
        return Math.max(0, leaveMin - arriveMax + 1);
    }

    private int getIdx(String s) {
        int month = Integer.parseInt(s.substring(0, 2));
        int day = Integer.parseInt(s.substring(3, 5));
        return preSum[month - 1] + day;
    }
}

T2. 运动员和训练师的最大匹配数(4 分)

解题思路

排序 + 双指针。

时间复杂度:O(nlogn)。为排序时间复杂度。

参考代码

class Solution {
    public int matchPlayersAndTrainers(int[] players, int[] trainers) {
        Arrays.sort(players);
        Arrays.sort(trainers);

        // 双指针
        int p1 = 0;
        int p2 = 0;
        int cnt = 0;
        while (p1 < players.length && p2 < trainers.length) {
            if (players[p1] <= trainers[p2]) {
                cnt++;
                p1++;
                p2++;
            } else {
                p2++;
            }
        }
        return cnt;
    }
}

T3. 按位或最大的最小子数组长度(5 分)

解题思路

难点在于需要在时间复杂度 O(1) 内知道区间的按位或,比赛时用了前缀和思想 + 二分查找求解。

时间复杂度:O(nlogn)。常数为 logmax(nums) 可看作 30

参考代码

class Solution {
    private static final int MAX_N = 31;

    public int[] smallestSubarrays(int[] nums) {
        int n = nums.length;

        int[][] bitCnt = new int[n][MAX_N];
        for (int i = 0; i < n; i++) {
            for (int k = 0; k < MAX_N; k++) {
                if (((nums[i] >> k) & 1) == 1) {
                    bitCnt[i][k]++;
                }
            }
        }

        int[][] preSum = new int[n + 1][MAX_N];
        for (int i = 0; i < n; i++) {
            for (int k = 0; k < MAX_N; k++) {
                preSum[i + 1][k] = preSum[i][k] + bitCnt[i][k];
            }
        }

        int[] res = new int[n];
        for (int i = 0; i < n; i++) {
            // [i, n-1] 最大按位或
            int max = 0;
            for (int k = 0; k < MAX_N; k++) {
                if (preSum[n][k] - preSum[i][k] > 0) {
                    max++;
                }
            }

            // 二分
            int left = i;
            int right = n;
            while (left < right) {
                int mid = left + (right - left) / 2;
                // 边界二分 F, F,..., F, [T, T,..., T]
                // ----------------------^
                if (checkMid(preSum, i, max, mid)) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
            res[i] = left - i + 1;
        }
        return res;
    }

    // FFFTTT
    private boolean checkMid(int[][] preSum, int i, int max, int mid) {
        int cnt = 0;
        for (int k = 0; k < MAX_N; k++) {
            if (preSum[mid + 1][k] - preSum[i][k] > 0) {
                cnt++;
            }
        }
        return cnt == max;
    }
}

灵佬直播分享了一种解法,时间复杂度:O(n*logmax(nuns)),空间复杂度:O(logmax(nuns)),消化中:

class Solution {
    public int[] smallestSubarrays(int[] nums) {
        int n = nums.length;

        int[] res = new int[n];
        List<int[]> ors = new ArrayList<>();
        for (int i = n - 1; i >= 0; i--) {
            int num = nums[i];
            ors.add(new int[]{0, i});
            int k = 0;
            for (int[] or : ors) {
                or[0] |= num;
                if (ors.get(k)[0] == or[0]) {
                    ors.get(k)[1] = or[1];
                } else {
                    k++;
                    ors.set(k, or);
                }
            }
            ors.subList(k + 1, ors.size()).clear();
            res[i] = ors.get(0)[1] - i + 1;
        }
        return res;
    }
}

T4. 完成所有交易的初始最少钱数(6 分)

解题思路

贪心。思考:如何才能尽可能亏得多?

对于亏钱的交易,亏钱多的越早越好,对于赚钱的交易,收益越高的越晚越好。先进行完所有亏钱交易,再进行赚钱交易。按顺序贪心取最小值即可。

时间复杂度:O(nlogn)。为排序时间复杂度。

参考代码

class Solution {
    public long minimumMoney(int[][] transactions) {
        List<int[]> negativeList = new ArrayList<>();
        List<int[]> positiveList = new ArrayList<>();
        for (int[] transaction : transactions) {
            if (transaction[1] - transaction[0] <= 0) {
                negativeList.add(transaction);
            } else {
                positiveList.add(transaction);
            }
        }
        negativeList.sort(Comparator.comparingInt(o -> o[1]));
        positiveList.sort((o1, o2) -> Integer.compare(o2[0], o1[0]));

        long money = 0;
        long min = Long.MAX_VALUE;
        for (int[] ints : negativeList) {
            money -= ints[0];
            min = Math.min(min, money);
            money += ints[1];
        }
        for (int[] ints : positiveList) {
            money -= ints[0];
            min = Math.min(min, money);
            money += ints[1];
        }
        return -min;
    }
}

(全文完)