100148. Minimum Number Game

你有一个下标从 0 开始、长度为 偶数 的整数数组 nums ,同时还有一个空数组 arr 。Alice 和 Bob 决定玩一个游戏,游戏中每一轮 Alice 和 Bob 都会各自执行一次操作。游戏规则如下:

  • 每一轮,Alice 先从 nums 中移除一个 最小 元素,然后 Bob 执行同样的操作。
  • 接着,Bob 会将移除的元素添加到数组 arr 中,然后 Alice 也执行同样的操作。
  • 游戏持续进行,直到 nums 变为空。

返回结果数组 arr 。

测试样例:

输入:nums = [2,5]

输出:[5,2]

解释:第一轮,Alice 先移除 2 ,然后 Bob 移除 5 。然后 Bob 先将 5 添加到 arr 中,接着 Alice 再将 2 添加到 arr 中。于是 arr = [5,2] 。

解答:排序之后,按照题目要求,奇偶位互换。

class Solution {
    public int[] numberGame(int[] nums) {
        int len = nums.length;
        int[] res = new int[len];
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        for (int n : nums) {
            queue.add(n);
        }
        int pos = 0;
        while (pos < res.length) {
            int a = queue.poll(), b = queue.poll();
            res[pos++] = b;
            res[pos++] = a;
        }
        return res;
    }
}

100169. Maximum Square Area by Removing Fences From a Field

有一个大型的 (m - 1) x (n - 1) 矩形田地,其两个对角分别是 (1, 1) 和 (m, n) ,田地内部有一些水平栅栏和垂直栅栏,分别由数组 hFences 和 vFences 给出。

水平栅栏为坐标 (hFences[i], 1) 到 (hFences[i], n),垂直栅栏为坐标 (1, vFences[i]) 到 (m, vFences[i]) 。

返回通过 移除 一些栅栏(可能不移除)所能形成的最大面积的 正方形 田地的面积,或者如果无法形成正方形田地则返回 -1。

由于答案可能很大,所以请返回结果对 109 + 7 取余 后的值。

注意:田地外围两个水平栅栏(坐标 (1, 1) 到 (1, n) 和坐标 (m, 1) 到 (m, n) )以及两个垂直栅栏(坐标 (1, 1) 到 (m, 1) 和坐标 (1, n) 到 (m, n) )所包围。这些栅栏 不能 被移除。

测试样例:

输入:m = 4, n = 3, hFences = [2,3], vFences = [2]

输出:4

解释:移除位于 2 的水平栅栏和位于 2 的垂直栅栏将得到一个面积为 4 的正方形田地。

解答:因为1 <= hFences.length, vFences.length <= 600,可以暴力枚举一下所有隔板去除的可能性,存在一个HashSet中。这样就能快速判断哪些是共有边长。寻找最长共有边长就可以了。

class Solution {
    private static final int mod = 1_000_000_007;

    public int maximizeSquareArea(int m, int n, int[] hFences, int[] vFences) {
        Set<Integer> d1 = fencesDis(m, hFences), d2 = fencesDis(n, vFences);
        long max = 0;
        for (int t : d1) {
            if (d2.contains(t)) {
                max = Math.max(max, t);
            }
        }
        if (max == 0) {
            return -1;
        }
        return (int) ((max * max) % mod);
    }

    private Set<Integer> fencesDis(int max, int[] fences) {
        Set<Integer> res = new HashSet<>();
        res.add(max - 1);
        for (int i = 0; i < fences.length; ++i) {
            res.add(fences[i] - 1);
            res.add(max - fences[i]);
            for (int j = 0; j < i; ++j) {
                res.add(Math.abs(fences[i] - fences[j]));
            }
        }
        return res;
    }
}

100156. Minimum Cost to Convert String I

给你两个下标从 0 开始的字符串 source 和 target ,它们的长度均为 n 并且由 小写 英文字母组成。

另给你两个下标从 0 开始的字符数组 original 和 changed ,以及一个整数数组 cost ,其中 cost[i] 代表将字符 original[i] 更改为字符 changed[i] 的成本。

你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == z 、original[j] == x 以及 changed[j] == y 。你就可以选择字符串中的一个字符 x 并以 z 的成本将其更改为字符 y 。

返回将字符串 source 转换为字符串 target 所需的 最小 成本。如果不可能完成转换,则返回 -1 。

注意,可能存在下标 i 、j 使得 original[j] == original[i] 且 changed[j] == changed[i] 。

测试样例:

输入:source = "abcd", target = "acbe", original = ["a","b","c","c","e","d"], changed = ["b","c","b","e","b","e"], cost = [2,5,5,1,2,20]

输出:28

解释:将字符串 "abcd" 转换为字符串 "acbe" :

  • 更改下标 1 处的值 'b' 为 'c' ,成本为 5 。
  • 更改下标 2 处的值 'c' 为 'e' ,成本为 1 。
  • 更改下标 2 处的值 'e' 为 'b' ,成本为 2 。
  • 更改下标 3 处的值 'd' 为 'e' ,成本为 20 。

产生的总成本是 5 + 1 + 2 + 20 = 28 。可以证明这是可能的最小成本。

解答:这题是最后一题的简化版本。本质上寻找两点之间最短距离,因为一共只有26个字符,可以直接暴力运算。

class Solution {
    private class PackedInfo {
        int[][] edges;
        long[][] floyd;

        public PackedInfo(char[] original, char[] changed, int[] cost) {
            edges = new int[26][26];
            for (int i = 0; i < 26; ++i) {
                for (int j = 0; j < 26; ++j) {
                    edges[i][j] = Integer.MAX_VALUE;
                }
            }
            for (int i = 0; i < original.length; ++i) {
                edges[original[i] - 'a'][changed[i] - 'a'] = Math.min(edges[original[i] - 'a'][changed[i] - 'a'], cost[i]);
            }
            floyd = initialFloyd(26);
        }

        private long[][] initialFloyd(int n) {
            long[][] res = new long[n][n];
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (i != j) res[i][j] = -1;
                }
            }
            return res;
        }

        public long getBestDis(int st, int ed) {
            if (floyd[st][ed] == -1) {
                for (int i = 0; i < floyd.length; ++i) {
                    floyd[st][i] = Long.MAX_VALUE;
                }

                TreeSet<Integer> queue = new TreeSet<>(new Comparator<Integer>() {
                    @Override
                    public int compare(Integer o1, Integer o2) {
                        Long s1 = floyd[st][o1], s2 = floyd[st][o2];
                        if (!s1.equals(s2)) {
                            return s1.compareTo(s2);
                        } else {
                            return o1.compareTo(o2);
                        }
                    }
                });

                floyd[st][st] = 0;
                queue.add(st);
                while (!queue.isEmpty()) {
                    int first = queue.first();
                    queue.remove(first);
                    for (int i = 0; i < 26; ++i) {
                        if (edges[first][i] != Integer.MAX_VALUE) {
                            if (floyd[st][i] > floyd[st][first] + edges[first][i]) {
                                queue.remove(i);
                                floyd[st][i] = floyd[st][first] + edges[first][i];
                                queue.add(i);
                            }
                        }
                    }
                }
            }
            return floyd[st][ed];
        }
    }

    public long minimumCost(String source, String target, char[] original, char[] changed, int[] cost) {
        if (source.equals(target)) {
            return 0;
        }

        int len = source.length();
        PackedInfo info = new PackedInfo(original, changed, cost);
        long[] dp = new long[len];
        Arrays.fill(dp, -1);
        for (int i = 0; i < len; ++i) {
            if (source.charAt(i) == target.charAt(i)) {
                if (i == 0) {
                    dp[i] = 0;
                    continue;
                } else {
                    dp[i] = dp[i - 1];
                }
            }

            int j = 1;
            if (i - j + 1 == 0 || dp[i - j] != -1) {
                long costChange = info.getBestDis(source.charAt(i) - 'a', target.charAt(i) - 'a');
                if (costChange != Long.MAX_VALUE) {
                    if (dp[i] == -1) {
                        dp[i] = Long.MAX_VALUE;
                    }
                    dp[i] = Math.min(dp[i], costChange + (i - j + 1 == 0 ? 0 : dp[i - j]));
                }
            }
        }
        return dp[len - 1];
    }
}

100158. Minimum Cost to Convert String II

给你两个下标从 0 开始的字符串 source 和 target ,它们的长度均为 n 并且由 小写 英文字母组成。

另给你两个下标从 0 开始的字符串数组 original 和 changed ,以及一个整数数组 cost ,其中 cost[i] 代表将字符串 original[i] 更改为字符串 changed[i] 的成本。

你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == z 、original[j] == x 以及 changed[j] == y ,你就可以选择字符串中的 子串 x 并以 z 的成本将其更改为 y 。 你可以执行 任意数量 的操作,但是任两次操作必须满足 以下两个 条件 之一 :

  • 在两次操作中选择的子串分别是 source[a..b] 和 source[c..d] ,满足 b < c 或 d < a 。换句话说,两次操作中选择的下标 不相交 。
  • 在两次操作中选择的子串分别是 source[a..b] 和 source[c..d] ,满足 a == c 且 b == d 。换句话说,两次操作中选择的下标 相同 。

返回将字符串 source 转换为字符串 target 所需的 最小 成本。如果不可能完成转换,则返回 -1 。

注意,可能存在下标 i 、j 使得 original[j] == original[i] 且 changed[j] == changed[i] 。

测试样例:

输入:source = "abcd", target = "acbe", original = ["a","b","c","c","e","d"], changed = ["b","c","b","e","b","e"], cost = [2,5,5,1,2,20]

输出:28

解释:将字符串 "abcd" 转换为字符串 "acbe" :

  • 将子串 source[1..1] 从 "b" 改为 "c" ,成本为 5 。
  • 将子串 source[2..2] 从 "c" 改为 "e" ,成本为 1 。
  • 将子串 source[2..2] 从 "e" 改为 "b" ,成本为 2 。
  • 将子串 source[3..3] 从 "d" 改为 "e" ,成本为 20 。

产生的总成本是 5 + 1 + 2 + 20 = 28 。可以证明这是可能的最小成本。

解答:操作需要满足2个条件,这两个条件暗示了可以使用动态规划。其他就和上一题比较类似了,寻找两点之间最短距离。

class Solution {
    private class PackedInfo {
        HashMap<String, Integer> wordOffset;
        HashMap<Integer, List<int[]>> edges;
        long[][] floyd;
        Set<Integer> length;
        String[] original, changed;
        int[] cost;

        public PackedInfo(String[] original, String[] changed, int[] cost) {
            this.original = original;
            this.changed = changed;
            this.cost = cost;
            length = new HashSet<>();
            edges = new HashMap<>();

            int offset = 0;
            wordOffset = new HashMap<>();
            for (int i = 0; i < original.length; ++i) {
                if (!wordOffset.containsKey(original[i])) {
                    wordOffset.put(original[i], offset++);
                }
                if (!wordOffset.containsKey(changed[i])) {
                    wordOffset.put(changed[i], offset++);
                }
                length.add(original[i].length());
                int p1 = wordOffset.get(original[i]), p2 = wordOffset.get(changed[i]);
                if (!edges.containsKey(p1)) {
                    edges.put(p1, new ArrayList<>());
                }
                edges.get(p1).add(new int[]{wordOffset.get(changed[i]), cost[i]});
            }
            floyd = initialFloyd(offset);
        }

        private long[][] initialFloyd(int n) {
            long[][] res = new long[n][n];
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (i != j) res[i][j] = -1;
                }
            }
            return res;
        }

        public long getBestDis(int st, int ed) {
            if (floyd[st][ed] == -1) {
                for (int i = 0; i < floyd.length; ++i) {
                    floyd[st][i] = Long.MAX_VALUE;
                }

                TreeSet<Integer> queue = new TreeSet<>(new Comparator<Integer>() {
                    @Override
                    public int compare(Integer o1, Integer o2) {
                        Long s1 = floyd[st][o1], s2 = floyd[st][o2];
                        if (!s1.equals(s2)) {
                            return s1.compareTo(s2);
                        } else {
                            return o1.compareTo(o2);
                        }
                    }
                });

                floyd[st][st] = 0;
                queue.add(st);
                while (!queue.isEmpty()) {
                    int first = queue.first();
                    queue.remove(first);
                    if (edges.containsKey(first)) {
                        for (int[] next : edges.get(first)) {
                            if (floyd[st][next[0]] > floyd[st][first] + next[1]) {
                                queue.remove(next[0]);
                                floyd[st][next[0]] = floyd[st][first] + next[1];
                                queue.add(next[0]);
                            }
                        }
                    }
                }
            }
            return floyd[st][ed];
        }
    }

    public long minimumCost(String source, String target, String[] original, String[] changed, int[] cost) {
        if (source.equals(target)) {
            return 0;
        }

        int len = source.length();
        PackedInfo info = new PackedInfo(original, changed, cost);
        long[] dp = new long[len];
        Arrays.fill(dp, -1);
        for (int i = 0; i < len; ++i) {
            if (source.charAt(i) == target.charAt(i)) {
                if (i == 0) {
                    dp[i] = 0;
                    continue;
                } else {
                    dp[i] = dp[i - 1];
                }
            }

            for (int j : info.length) {
                if (i + 1 - j >= 0) {
                    String key = source.substring(i - j + 1, i + 1);
                    String end = target.substring(i - j + 1, i + 1);
                    if (info.wordOffset.containsKey(key) && info.wordOffset.containsKey(end) &&
                            (i - j + 1 == 0 || dp[i - j] != -1)) {
                        long costChange = info.getBestDis(info.wordOffset.get(key), info.wordOffset.get(end));
                        if (costChange != Long.MAX_VALUE) {
                            if (dp[i] == -1) {
                                dp[i] = Long.MAX_VALUE;
                            }
                            dp[i] = Math.min(dp[i], costChange + (i - j + 1 == 0 ? 0 : dp[i - j]));
                        }
                    }
                }
            }
        }
        return dp[len - 1];
    }
}

Leave a Reply

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