6470. Neither Minimum nor Maximum

给你一个整数数组 nums ,数组由 不同正整数 组成,请你找出并返回数组中 任一 既不是 最小值 也不是 最大值 的数字,如果不存在这样的数字,返回 -1 。

返回所选整数。

测试样例:

输入:nums = [3,2,1,4]

输出:2

解释:
在这个示例中,最小值是 1 ,最大值是 4 。因此,2 或 3 都是有效答案。

解答:两次遍历。第一次寻找最大值和最小值,第二次寻找第一个同时不是最小也不是最大的值。数据范围很小,直接暴力遍历。

class Solution {
    public int findNonMinOrMax(int[] nums) {
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for (int n : nums) {
            min = Math.min(min, n);
            max = Math.max(max, n);
        }

        for (int n : nums) {
            if (n != min && n != max) {
                return n;
            }
        }
        return -1;
    }
}

6465. Lexicographically Smallest String After Substring Operation

给你一个仅由小写英文字母组成的字符串 s 。在一步操作中,你可以完成以下行为:

  • 选则 s 的任一非空子字符串,可能是整个字符串,接着将字符串中的每一个字符替换为英文字母表中的前一个字符。例如,'b' 用 'a' 替换,'a' 用 'z' 替换。

返回执行上述操作 恰好一次 后可以获得的 字典序最小 的字符串。

子字符串 是字符串中的一个连续字符序列。

现有长度相同的两个字符串 x 和 字符串 y ,在满足 x[i] != y[i] 的第一个位置 i 上,如果 x[i] 在字母表中先于 y[i] 出现,则认为字符串 x 比字符串 y 字典序更小 。

测试样例:

输入:s = "cbabc"

输出:"baabc"

解释:
我们选择从下标 0 开始、到下标 1 结束的子字符串执行操作。 可以证明最终得到的字符串是字典序最小的。

解答:这道题目需要一点思考。它需要寻找第一个非a的元素,然后连续递减1,知道碰到下一个a元素。有一点需要注意,因为题目要求恰好一次操作。如果这个字符串全部有a构成,那么最后一个字母需要变成z。

class Solution {
    public String smallestString(String s) {
        boolean isUsed = false;
        boolean isPerform = false;
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < s.length(); ++i) {
            if (isPerform) {
                if (s.charAt(i) == 'a') {
                    isPerform = false;
                    res.append('a');
                } else {
                    res.append((char)(s.charAt(i) - 1));
                }
            } else {
                if (isUsed) {
                    res.append(s.charAt(i));
                } else {
                    if (s.charAt(i) == 'a') {
                        res.append('a');
                    } else {
                        isPerform = true;
                        isUsed = true;
                        res.append((char)(s.charAt(i) - 1));
                    }
                }
            }
        }
        return isUsed ? res.toString() : (s.substring(0, s.length() - 1) + 'z');
    }
}

6449. Collecting Chocolates

给你一个长度为 n 、下标从 0 开始的整数数组 nums ,表示收集不同巧克力的成本。每个巧克力都对应一个不同的类型,最初,位于下标 i 的巧克力就对应第 i 个类型。

在一步操作中,你可以用成本 x 执行下述行为:

  • 同时对于所有下标 0 <= i < n - 1 进行以下操作, 将下标 i 处的巧克力的类型更改为下标 (i + 1) 处的巧克力对应的类型。如果 i == n - 1 ,则该巧克力的类型将会变更为下标 0 处巧克力对应的类型。

假设你可以执行任意次操作,请返回收集所有类型巧克力所需的最小成本。

测试样例

输入:nums = [1,2,3], x = 4

输出:6

解释:
我们将会按最初的成本收集全部三个类型的巧克力,而不需执行任何操作。因此,收集所有类型的巧克力需要的总成本是 1 + 2 + 3 = 6 。

解答:这道题目有点难度,很容易被卡常。其实最多进行nums.length次数操作。所以我们用一个数组记录如果进行i次操作,能够达到的最小成本。然后利用优先队列记录,在i次操作过程中,某一个type能到获得最小成本。最后寻找所有操作可能中,最小值。

class Solution {
    public long minCost(int[] nums, long x) {
        long[] mem = new long[nums.length];
        for (int i = 0; i < nums.length; ++i) {
            mem[i] = x * i;
        }
        for (int i = 0; i < nums.length; ++i) {
            PriorityQueue<Integer> queue = new PriorityQueue<>();
            for (int j = 0; j < nums.length; ++j) {
                queue.add(nums[(i + j) % nums.length]);
                mem[j] += queue.peek();
            }
        }

        long min = Long.MAX_VALUE;
        for (long m : mem) {
            min = Math.min(min, m);
        }
        return min;
    }
}

6473. Maximum Sum Queries

给你两个长度为 n 、下标从 0 开始的整数数组 nums1 和 nums2 ,另给你一个下标从 1 开始的二维数组 queries ,其中 queries[i] = [xi, yi] 。

对于第 i 个查询,在所有满足 nums1[j] >= xi 且 nums2[j] >= yi 的下标 j (0 <= j < n) 中,找出 nums1[j] + nums2[j] 的 最大值 ,如果不存在满足条件的 j 则返回 -1 。

返回数组 answer ,其中 answer[i] 是第 i 个查询的答案。

测试样例

输入:nums1 = [3,2,5], nums2 = [2,3,4], queries = [[4,4],[3,2],[1,1]]

输出:[9,9,9]

解释:对于这个示例,我们可以选择下标 j = 2 ,该下标可以满足每个查询的限制。

解答:又是二维数组的题目。我们首先把nums1从大到小排序,然后在把queries按照queries[0]的大小从大到小排序。这时候我们遍历queries,并且维护一个nums1的迭代器,确保只有nums1[p] > queries[i][0]的记录可以加入比较。

但是这个时候问题又来了,我们还需要保证nums2[j] >= queries[i][1]。关于这个方面,我没想到什么好的办法。我能想到的是利用线段树,记录nums1的条件能满足的时候,所有能加入的nums2值。然后利用线段数,查询当前满足条件的最大和。幸好这道题目对算法复杂度的要求不是很高,这样也能AC。

class Solution {
    private class SegmentTree {
        int[] tree;
        int n;
        public SegmentTree(int len) {
            if (len > 0) {
                n = len;
                tree = new int[n * 2];
                for (int i = 0; i < tree.length; ++i) {
                    tree[i] = -1;
                }
            }
        }

        void update(int pos, int val) {
            pos += n;
            tree[pos] = val;
            while (pos > 0) {
                int left = pos;
                int right = pos;
                if (pos % 2 == 0) {
                    right = pos + 1;
                } else {
                    left = pos - 1;
                }
                // parent is updated after child is updated
                tree[pos / 2] = Math.max(tree[left], tree[right]);
                pos /= 2;
            }
        }

        public int maxRange(int l, int r) {
            l += n;
            r += n;
            int max = -1;
            while (l <= r) {
                if ((l % 2) == 1) {
                    max = Math.max(max, tree[l]);
                    l++;
                }
                if ((r % 2) == 0) {
                    max = Math.max(max, tree[r]);
                    r--;
                }
                l /= 2;
                r /= 2;
            }
            return max;
        }
    }

    public int[] maximumSumQueries(int[] nums1, int[] nums2, int[][] queries) {
        TreeMap<Integer, Integer> pos2 = findNums2Pos(nums2);
        int len = nums1.length;
        Integer[] nums1Sort = new Integer[len];
        for (int i = 0; i < len; ++i) {
            nums1Sort[i] = i;
        }
        Arrays.sort(nums1Sort, (a, b) -> (nums1[b] - nums1[a]));

        Integer[] querySort = new Integer[queries.length];
        for (int i = 0; i < querySort.length; ++i) {
            querySort[i] = i;
        }

        Arrays.sort(querySort, (a, b) -> (queries[b][0] - queries[a][0]));
        SegmentTree tree = new SegmentTree(pos2.size());
        int[] res = new int[queries.length];
        int p = 0;
        for (int i : querySort) {
            int[] q = queries[i];

            while (p < len && nums1[nums1Sort[p]] >= q[0]) {
                int n2 = nums2[nums1Sort[p]];
                int add = nums1[nums1Sort[p]] + n2;
                int point = pos2.get(n2);
                int oldSum = tree.maxRange(point, point);
                if (add > oldSum) {
                    tree.update(point, add);
                }
                ++p;
            }

            Map.Entry<Integer, Integer> high = pos2.ceilingEntry(q[1]);
            if (high == null) {
                res[i] = -1;
            } else {
                res[i] = tree.maxRange(high.getValue(), pos2.size() - 1);
            }
        }
        return res;
    }

    private TreeMap<Integer, Integer> findNums2Pos(int[] nums) {
        int[] dup = new int[nums.length];
        for (int i = 0; i < nums.length; ++i) {
            dup[i] = nums[i];
        }
        Arrays.sort(dup);
        int offset = 0;
        TreeMap<Integer, Integer> res = new TreeMap<>();
        for (int n : dup) {
            if (!res.containsKey(n)) {
                res.put(n, offset++);
            }
        }
        return res;
    }
}

这周题目,还是挺有难度的。。。

Leave a Reply

Your email address will not be published. Required fields are marked *