6925. Faulty Keyboard

你的笔记本键盘存在故障,每当你在上面输入字符 'i' 时,它会反转你所写的字符串。而输入其他字符则可以正常工作。

给你一个下标从 0 开始的字符串 s ,请你用故障键盘依次输入每个字符。

返回最终笔记本屏幕上输出的字符串。

测试样例:

输入:s = "string"

输出:"rtsng"

解释:

  • 输入第 1 个字符后,屏幕上的文本是:"s" 。
  • 输入第 2 个字符后,屏幕上的文本是:"st" 。
  • 输入第 3 个字符后,屏幕上的文本是:"str" 。
  • 因为第 4 个字符是 'i' ,屏幕上的文本被反转,变成 "rts" 。
  • 输入第 5 个字符后,屏幕上的文本是:"rtsn" 。
  • 输入第 6 个字符后,屏幕上的文本是: "rtsng" 。

因此,返回 "rtsng" 。

解答:按照题意暴力计算。

class Solution {
    public String finalString(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            if (c == 'i') {
                sb.reverse();
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

6953. Check if it is Possible to Split Array

给你一个长度为 n 的数组 nums 和一个整数 m 。请你判断能否执行一系列操作,将数组拆分成 n 个 非空 数组。

在每一步操作中,你可以选择一个 长度至少为 2 的现有数组(之前步骤的结果) 并将其拆分成 2 个子数组,而得到的 每个 子数组,至少 需要满足以下条件之一:

  • 子数组的长度为 1 ,或者
  • 子数组元素之和 大于或等于 m 。

如果你可以将给定数组拆分成 n 个满足要求的数组,返回 true ;否则,返回 false 。

注意:子数组是数组中的一个连续非空元素序列。

测试样例:

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

输出:true

解释:

  • 第 1 步,将数组 nums 拆分成 [2, 2] 和 [1] 。
  • 第 2 步,将数组 [2, 2] 拆分成 [2] 和 [2] 。

因此,答案为 true 。

解答:这道题目有点脑筋急转弯,如果数组中有连续2个数字之和大于m,就一定可以完成分裂。但是需要注意一个条件:子数组的长度为 1。所以nums.size <= 2的时候也一定可以分裂。WA麻了。

class Solution {
    public boolean canSplitArray(List<Integer> nums, int m) {
        if (nums.size() <= 2) {
            return true;
        }
        int pre = Integer.MIN_VALUE;
        for (int n : nums) {
            if (n + pre >= m) {
                return true;
            }
            pre = n;
        }
        return false;
    }
}

6951. Find the Safest Path in a Grid

给你一个下标从 0 开始、大小为 n x n 的二维矩阵 grid ,其中 (r, c) 表示:

  • 如果 grid[r][c] = 1 ,则表示一个存在小偷的单元格
  • 如果 grid[r][c] = 0 ,则表示一个空单元格

你最开始位于单元格 (0, 0) 。在一步移动中,你可以移动到矩阵中的任一相邻单元格,包括存在小偷的单元格。

矩阵中路径的 安全系数 定义为:从路径中任一单元格到矩阵中任一小偷所在单元格的 最小 曼哈顿距离。

返回所有通向单元格 (n - 1, n - 1) 的路径中的 最大安全系数 。

单元格 (r, c) 的某个 相邻 单元格,是指在矩阵中存在的 (r, c + 1)、(r, c - 1)、(r + 1, c) 和 (r - 1, c) 之一。

两个单元格 (a, b) 和 (x, y) 之间的 曼哈顿距离 等于 | a - x | + | b - y | ,其中 |val| 表示 val 的绝对值。

测试样例

输入:grid = [[1,0,0],[0,0,0],[0,0,1]]

输出:0

解释:
从 (0, 0) 到 (n - 1, n - 1) 的每条路径都经过存在小偷的单元格 (0, 0) 和 (n - 1, n - 1) 。

解答:其实感觉这道题目就不好做了。。。这道题目需要逆向思维。我们可以分析一下每个点到最近的小偷曼哈顿距离,并且做出一个列表。然后我们距离由远及近,利用并查集查看起始和结束点是不是可达。

class Solution {
    class DSU{
        int[] parent;

        public DSU(int N) {
            parent = new int[N];
            for (int i = 0; i < N; ++i) {
                parent[i] = i;
            }
        }

        public int find(int x) {
            if (parent[x] != x) parent[x] = find(parent[x]);
            return parent[x];
        }

        public void union(int x, int y) {
            parent[find(x)] = find(y);
        }
    }

    private static final int[] moves = {1,0,-1,0,1};

    public int maximumSafenessFactor(List<List<Integer>> grid) {
        int[][] matrix = turnArrayList(grid);
        int n = matrix.length;
        if (matrix.length == 1 && matrix[0].length == 1 && matrix[0][0] == 1) {
            return 0;
        }
        LinkedList<int[]> list = new LinkedList<>();
        List<int[]> last = new ArrayList<>();
        boolean[][] isMatched = new boolean[n][n];
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 1) {
                    list.addFirst(new int[]{i, j});
                    last.add(new int[]{i, j});
                    isMatched[i][j] = true;
                }
            }
        }

        int maxDis = 0;
        while (!last.isEmpty()) {
            ++maxDis;
            list.addFirst(null);
            List<int[]> nextLast = new ArrayList<>();
            for (int[] l : last) {
                for (int i = 0; i < 4; ++i) {
                    int xt = l[0] + moves[i], yt = l[1] + moves[i + 1];
                    if (xt >= 0 && xt < n && yt >= 0 && yt < n && !isMatched[xt][yt]) {
                        isMatched[xt][yt] = true;
                        list.addFirst(new int[]{xt, yt});
                        nextLast.add(new int[]{xt, yt});
                    }
                }
            }
            last = nextLast;
        }

        DSU uf = new DSU(n * n);
        Iterator<int[]> it = list.iterator();
        isMatched = new boolean[n][n];

        for (int i = maxDis; i >= 0; --i) {
            while (it.hasNext()) {
                int[] tmp = it.next();
                if (tmp == null) {
                    break;
                } else {
                    isMatched[tmp[0]][tmp[1]] = true;
                    for (int mv = 0; mv < 4; ++mv) {
                        int xt = tmp[0] + moves[mv], yt = tmp[1] + moves[mv + 1];
                        if (xt >= 0 && xt < n && yt >= 0 && yt < n && isMatched[xt][yt]) {
                            uf.union(xt * n + yt, tmp[0] * n + tmp[1]);
                        }
                    }
                }
            }
            if (uf.find(0) == uf.find((n - 1) * n + n - 1)) {
                return i;
            }
        }
        return 0;
    }

    private int[][] turnArrayList(List<List<Integer>> grid) {
        int n = grid.size(), offset = 0;
        int[][] res = new int[n][n];
        for (List<Integer> m : grid) {
            int to = 0;
            for (Integer num : m) {
                res[offset][to++] = num;
            }
            ++offset;
        }
        return res;
    }
}

6932. Maximum Elegance of a K-Length Subsequence

给你一个长度为 n 的二维整数数组 items 和一个整数 k 。

items[i] = [profiti, categoryi],其中 profiti 和 categoryi 分别表示第 i 个项目的利润和类别。

现定义 items 的 子序列 的 优雅度 可以用 total_profit + distinct_categories2 计算,其中 total_profit 是子序列中所有项目的利润总和,distinct_categories 是所选子序列所含的所有类别中不同类别的数量。

你的任务是从 items 所有长度为 k 的子序列中,找出 最大优雅度 。

用整数形式表示并返回 items 中所有长度恰好为 k 的子序列的最大优雅度。

注意:数组的子序列是经由原数组删除一些元素(可能不删除)而产生的新数组,且删除不改变其余元素相对顺序。

测试样例

输入:items = [[3,2],[5,1],[10,1]], k = 2

输出:17

解释:在这个例子中,我们需要选出长度为 2 的子序列。其中一种方案是 items[0] = [3,2] 和 items[2] = [10,1] 。子序列的总利润为 3 + 10 = 13 ,子序列包含 2 种不同类别 [2,1] 。因此,优雅度为 13 + 22 = 17 ,可以证明 17 是可以获得的最大优雅度。

解答:我们首先利用排序,将profit从大到小遍历,然后开始遍历整个数组。需要注意的地方是,如果加入的数字已经达到k个,那么就需要尽量增加新的数字可能(已经按照profit排序,所以此时profit一定是最优的)。

class Solution {
    private HashMap<Integer, PriorityQueue<Integer>> queue;
    private TreeMap<Integer, HashMap<Integer, Integer>> scoreSort;

    public long findMaximumElegance(int[][] items, int k) {
        Arrays.sort(items, (a, b) -> (b[0] - a[0]));

        queue = new HashMap<>();
        scoreSort = new TreeMap<>();

        long res = 0, profitSum = 0;
        for (int[] item : items) {
            int cat = item[1], pro = item[0];
            if (k > 0) {
                profitSum += pro;
                addQueue(cat, pro);
                addScoreWithCondition(cat, pro);
                --k;
            } else if (!queue.containsKey(cat) && !scoreSort.isEmpty()) {
                int lower = scoreSort.firstKey(), group = getRandomGroup(lower);
                removeScore(group, queue.get(group).poll());
                if (queue.get(group).size() == 1) {
                    removeScore(group, queue.get(group).peek());
                }
                addQueue(cat, pro);
                addScoreWithCondition(cat, pro);
                profitSum -= lower;
                profitSum += pro;
            }

            long distinct = queue.size();
            res = Math.max(res, distinct * distinct + profitSum);
        }
        return res;
    }

    private void addQueue(int category, int profit) {
        if (!queue.containsKey(category)) {
            queue.put(category, new PriorityQueue<>());
        }
        queue.get(category).add(profit);
    }

    private void addScoreWithCondition(int group, int score) {
        if (queue.get(group).size() == 2) {
            for (int s : queue.get(group)) {
                addScore(group, s);
            }
        } else if (queue.get(group).size() > 2) {
            addScore(group, score);
        }
    }

    private void addScore(int group, int score) {
        if (!scoreSort.containsKey(score)) {
            scoreSort.put(score, new HashMap<>());
        }
        HashMap<Integer, Integer> map = scoreSort.get(score);
        map.put(group, map.getOrDefault(group, 0) + 1);
    }

    private int getRandomGroup(int group) {
        for (int n : scoreSort.get(group).keySet()) {
            return n;
        }
        return -1;
    }

    private void removeScore(int group, int score) {
        HashMap<Integer, Integer> tmp = scoreSort.get(score);
        int old = tmp.get(group);
        if (old == 1) {
            tmp.remove(group);
        } else {
            tmp.put(group, old - 1);
        }
        if (tmp.isEmpty()) {
            scoreSort.remove(score);
        }
    }
}

Leave a Reply

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