2566.替换一个数字后的最大差值

目标

给你一个整数 num 。你知道 Bob 会偷偷将 0 到 9 中的一个数字 替换 成另一个数字。

请你返回将 num 中 恰好一个 数字进行替换后,得到的最大值和最小值的差为多少。

注意:

  • 当 Bob 将一个数字 d1 替换成另一个数字 d2 时,Bob 需要将 nums 中所有 d1 都替换成 d2 。
  • Bob 可以将一个数字替换成它自己,也就是说 num 可以不变。
  • Bob 可以将数字分别替换成两个不同的数字分别得到最大值和最小值。
  • 替换后得到的数字可以包含前导 0 。

示例 1:

输入:num = 11891
输出:99009
解释:
为了得到最大值,我们将数字 1 替换成数字 9 ,得到 99899 。
为了得到最小值,我们将数字 1 替换成数字 0 ,得到 890 。
两个数字的差值为 99009 。

示例 2:

输入:num = 90
输出:99
解释:
可以得到的最大值是 99(将 0 替换成 9),最小值是 0(将 9 替换成 0)。
所以我们得到 99 。

说明:

  • 1 <= num <= 10^8

思路

num 中选择一个数字 d,可以将 num 中所有的 d 都替换成另一个数字(允许包含前导零),返回能够得到的最大值与最小值的差。

贪心策略,找到第一个不是 9 的数字 a,将 num 中所有 a 都替换为 9 得到最大数字。定义 num 第一个数字为 b,将 num 中的所有 b 替换为 0 即为最小数字。

代码


/**
 * @date 2025-06-14 9:49
 */
public class MinMaxDifference2566 {

    public int minMaxDifference(int num) {
        String s = String.valueOf(num);
        char[] chars = s.toCharArray();
        int n = s.length();
        int mul = (int) Math.pow(10, n - 1);
        int max = num, min = num;
        char maxReplace = '-';
        char minReplace = chars[0];
        for (int i = 0; i < n; i++) {
            if (maxReplace == '-' && chars[i] != '9') {
                maxReplace = chars[i];
            }
            if (minReplace == chars[i]) {
                min -= mul * (chars[i] - '0');
            }
            if (maxReplace == chars[i]) {
                max += mul * (9 - (chars[i] - '0'));
            }
            mul /= 10;
        }
        return max - min;
    }

}

性能

2616.最小化数对的最大差值

目标

给你一个下标从 0 开始的整数数组 nums 和一个整数 p 。请你从 nums 中找到 p 个下标对,每个下标对对应数值取差值,你需要使得这 p 个差值的 最大值 最小。同时,你需要确保每个下标在这 p 个下标对中最多出现一次。

对于一个下标对 i 和 j ,这一对的差值为 |nums[i] - nums[j]| ,其中 |x| 表示 x 的 绝对值 。

请你返回 p 个下标对对应数值 最大差值 的 最小值 。

示例 1:

输入:nums = [10,1,2,7,1,3], p = 2
输出:1
解释:第一个下标对选择 1 和 4 ,第二个下标对选择 2 和 5 。
最大差值为 max(|nums[1] - nums[4]|, |nums[2] - nums[5]|) = max(0, 1) = 1 。所以我们返回 1 。

示例 2:

输入:nums = [4,2,1,2], p = 1
输出:0
解释:选择下标 1 和 3 构成下标对。差值为 |2 - 2| = 0 ,这是最大差值的最小值。

说明:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^9
  • 0 <= p <= (nums.length)/2

思路

p 个不包含相同下标的下标对,使这 p 个下标对的绝对差的最大值最小。

最小化最大值,优先考虑二分查找。问题是如何判断绝对差小于当前值的数对个数。

参考 2560.打家劫舍IV,可以使用动态规划或者贪心。

关键是贪心策略,只要相邻元素的绝对差小于二分的最大绝对差就是可选的

代码


/**
 * @date 2025-06-13 0:09
 */
public class MinimizeMax2616 {

    public int minimizeMax(int[] nums, int p) {
        Arrays.sort(nums);
        int r = nums[nums.length - 1] - nums[0];
        int l = 0;
        int mid = l + (r - l) / 2;
        while (l <= r) {
            if (check(nums, p, mid)) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
            mid = l + (r - l) / 2;
        }
        return l;
    }

    public boolean check(int[] nums, int p, int cur) {
        int n = nums.length;
        for (int i = 1; i < n && p > 0; i++) {
            if (nums[i] - nums[i - 1] <= cur) {
                i++;
                p--;
            }
        }
        return p == 0;
    }

}

性能

3170.删除星号以后字典序最小的字符串

目标

给你一个字符串 s 。它可能包含任意数量的 '' 字符。你的任务是删除所有的 '' 字符。

当字符串还存在至少一个 '*' 字符时,你可以执行以下操作:

  • 删除最左边的 '*' 字符,同时删除该星号字符左边一个字典序 最小 的字符。如果有多个字典序最小的字符,你可以删除它们中的任意一个。

请你返回删除所有 '*' 字符以后,剩余字符连接而成的 字典序最小 的字符串。

示例 1:

输入:s = "aaba*"
输出:"aab"
解释:
删除 '*' 号和它左边的其中一个 'a' 字符。如果我们选择删除 s[3] ,s 字典序最小。

示例 2:

输入:s = "abc"
输出:"abc"
解释:
字符串中没有 '*' 字符。

说明:

  • 1 <= s.length <= 10^5
  • s 只含有小写英文字母和 '*' 字符。
  • 输入保证操作可以删除所有的 '*' 字符。

思路

有一个包含任意数量 * 的字符串,每次操作可以删掉 * 以及它左侧的一个字典序最小的字符,如果有多个可以,删除任意一个。求删除所有 * 之后能够得到的 字典序最小的字符串。

贪心算法,优先删除左侧字典序最小且下标最大的字符即可。

代码


/**
 * @date 2025-06-07 9:37
 */
public class ClearStars3170 {

    public String clearStars(String s) {
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> {
            int compare = a[0] - b[0];
            if (compare != 0) {
                return compare;
            }
            return b[1] - a[1];
        });
        char[] chars = s.toCharArray();
        int n = chars.length;
        for (int i = 0; i < n; i++) {
            char c = chars[i];
            if (c != '*') {
                q.offer(new int[]{c, i});
            } else {
                q.poll();
            }
        }
        char[] res = new char[q.size()];
        PriorityQueue<int[]> tmp = new PriorityQueue<>((a, b) -> a[1] - b[1]);
        tmp.addAll(q);
        int i = 0;
        while (!tmp.isEmpty()) {
            int[] c = tmp.poll();
            res[i++] = (char) c[0];
        }
        return new String(res);
    }
}

性能

2434.使用机器人打印字典序最小的字符串

目标

给你一个字符串 s 和一个机器人,机器人当前有一个空字符串 t 。执行以下操作之一,直到 s 和 t 都变成空字符串:

  • 删除字符串 s 的 第一个 字符,并将该字符给机器人。机器人把这个字符添加到 t 的尾部。
  • 删除字符串 t 的 最后一个 字符,并将该字符给机器人。机器人将该字符写到纸上。

请你返回纸上能写出的字典序最小的字符串。

示例 1:

输入:s = "zza"
输出:"azz"
解释:用 p 表示写出来的字符串。
一开始,p="" ,s="zza" ,t="" 。
执行第一个操作三次,得到 p="" ,s="" ,t="zza" 。
执行第二个操作三次,得到 p="azz" ,s="" ,t="" 。

示例 2:

输入:s = "bac"
输出:"abc"
解释:用 p 表示写出来的字符串。
执行第一个操作两次,得到 p="" ,s="c" ,t="ba" 。
执行第二个操作两次,得到 p="ab" ,s="c" ,t="" 。
执行第一个操作,得到 p="ab" ,s="" ,t="c" 。
执行第二个操作,得到 p="abc" ,s="" ,t="" 。

示例 3:

输入:s = "bdda"
输出:"addb"
解释:用 p 表示写出来的字符串。
一开始,p="" ,s="bdda" ,t="" 。
执行第一个操作四次,得到 p="" ,s="" ,t="bdda" 。
执行第二个操作四次,得到 p="addb" ,s="" ,t="" 。

说明:

  • 1 <= s.length <= 10^5
  • s 只包含小写英文字母。

思路

  • 首先统计字符串中的字符个数,要使输出的字典序最小,那么第一个字符一定是字符串中出现过的字典序最小的字符
  • 为了将该字符打印到首位,需要先定位到这个字符,它前面的字符都会被暂存到栈中
  • 确定了第一个字典序最小的字符之后,下一个字符有两个选择,取栈顶字符,或者找到剩下的字符中的字典序最小的

需要维护剩余字符中最小字典序的字符,可以使用有序集合或者后缀。

代码


/**
 * @date 2025-06-06 8:47
 */
public class RobotWithString2434 {

    public String robotWithString(String s) {
        TreeMap<Character, Integer> map = new TreeMap<>();
        char[] chars = s.toCharArray();
        int n = chars.length;
        for (char c : chars) {
            map.merge(c, 1, Integer::sum);
        }
        ArrayDeque<Character> q = new ArrayDeque<>();
        StringBuilder sb = new StringBuilder();
        int i = 0;
        while (i < n) {
            Character min = map.firstKey();
            while (i < n && chars[i] != min) {
                char c = chars[i++];
                q.offer(c);
                map.merge(c, -1, Integer::sum);
                if (map.get(c) == 0) {
                    map.remove(c);
                }
            }
            sb.append(min);
            map.merge(min, -1, Integer::sum);
            if (map.get(min) == 0) {
                map.remove(min);
            }
            i++;
            while (!q.isEmpty() && (map.size() == 0 || q.peekLast() <= map.firstKey())) {
                sb.append(q.pollLast());
            }
        }
        return sb.toString();
    }

}

性能

3403.从盒子中找出字典序最大的字符串I

目标

给你一个字符串 word 和一个整数 numFriends。

Alice 正在为她的 numFriends 位朋友组织一个游戏。游戏分为多个回合,在每一回合中:

  • word 被分割成 numFriends 个 非空 字符串,且该分割方式与之前的任意回合所采用的都 不完全相同 。
  • 所有分割出的字符串都会被放入一个盒子中。

在所有回合结束后,找出盒子中 字典序最大的 字符串。

示例 1:

输入: word = "dbca", numFriends = 2
输出: "dbc"
解释: 
所有可能的分割方式为:
"d" 和 "bca"。
"db" 和 "ca"。
"dbc" 和 "a"。

示例 2:

输入: word = "gggg", numFriends = 4
输出: "g"
解释: 
唯一可能的分割方式为:"g", "g", "g", 和 "g"。

提示:

  • 1 <= word.length <= 5 * 10^3
  • word 仅由小写英文字母组成。
  • 1 <= numFriends <= word.length

思路

word 划分为 numFriends 个非空子串,求字典序最大的子串。

找字典序最大的首字母,如果存在多个需要比较后面字符的字典序,还要保证能够划分成非空子串。

先找到字符串中字典序最大的字符集合作为起点,将其后面的字符放入优先队列 [char, index],根据字符从大到小排序,取队首连续相同的字符,将其后一个字符放到下一轮处理。

也可以不用手动逐个比较字符,枚举字典序最大的字符作为左端点,然后尽可能地扩展字符串长度 n - numFriends + 1,将结果收集后排序即可。

代码


/**
 * @date 2025-06-04 9:19
 */
public class AnswerString3403 {

    public String answerString(String word, int numFriends) {
        if (numFriends == 1) {
            return word;
        }
        int n = word.length();
        List<Integer>[] chars = new ArrayList[26];
        Arrays.setAll(chars, x -> new ArrayList<>());
        for (int i = 0; i < n; i++) {
            char c = word.charAt(i);
            chars[c - 'a'].add(i);
        }
        int l = n - numFriends + 1;
        String[] strs = null;
        for (int i = 25; i >= 0; i--) {
            if (chars[i].size() > 0) {
                strs = new String[chars[i].size()];
                for (int j = 0; j < chars[i].size(); j++) {
                    int index = chars[i].get(j);
                    strs[j] = word.substring(index, Math.min(index + l, n));
                }
                break;
            }
        }
        Arrays.sort(strs);
        return strs[strs.length - 1];
    }

}

性能

135.分发糖果

目标

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

说明:

  • n == ratings.length
  • 1 <= n <= 2 * 10^4
  • 0 <= ratings[i] <= 2 * 10^4

思路

n 个孩子站成一排,ratings 表示每个孩子的评分。现在需要给孩子分发糖果,要求每个孩子至少分配一个糖果,并且相邻的两个孩子,评分高的孩子分得的糖果大于评分低的孩子分得的糖果,返回需要准备的最少糖果数目。

官网题解提供了一种常数空间复杂度的写法,记录评分严格递增与严格递减序列长度:

  • 当处于严格递增序列时,分配的糖果比前面的多一个即可
  • 如果评分相同,则分配一个糖果并重置序列长度
  • 当处于严格递减序列时,分配递减序列长度个糖果,相当于递增序列的逆过程
  • 需要特殊处理递增序列与递减序列长度相同的情况

看下面的例子:

评分 4 5 4 3 2 1,
    a b c d e f
    1 2 1
当处理到 c 时,处于下降序列,我们给他分配 1 个糖果
    1 3 2 1
当处理到 d 时,仍处于下降序列,需要给 c 多分配一个糖果,即 2 个,发现这时与 b 分配的相同,不满足要求,需要给 b 也多分配一个,共 3 个
    1 4 3 2 1
当处理到 e 时,仍处于下降序列,需要给 b c d 多分配一个,共 4 个
    1 5 4 3 2 1
当处理到 f 时,仍处于下降序列,需要给 b c d e 多分配一个糖果,共 5 个

相当于往递减序列的头部插入递减序列长度个糖果,视为反向的递增,当递增序列与递减序列长度相同时需要特殊处理。

代码


/**
 * @date 2024-12-11 9:18
 */
public class Candy135 {

    public int candy_v3(int[] ratings) {
        int n = ratings.length;
        int increase = 1;
        int decrease = 0;
        int res = 1;
        int prev = 1;
        for (int i = 1; i < n; i++) {
            if (ratings[i] >= ratings[i - 1]) {
                prev = ratings[i] == ratings[i - 1] ? 1 : prev + 1;
                res += prev;
                increase = prev;
                decrease = 0;
            } else {
                decrease++;
                if (decrease == increase) {
                    decrease++;
                }
                res += decrease;
                prev = 1;
            }
        }
        return res;
    }

}

性能

2131.连接两字母单词得到的最长回文串

目标

给你一个字符串数组 words 。words 中每个元素都是一个包含 两个 小写英文字母的单词。

请你从 words 中选择一些元素并按 任意顺序 连接它们,并得到一个 尽可能长的回文串 。每个元素 至多 只能使用一次。

请你返回你能得到的最长回文串的 长度 。如果没办法得到任何一个回文串,请你返回 0 。

回文串 指的是从前往后和从后往前读一样的字符串。

示例 1:

输入:words = ["lc","cl","gg"]
输出:6
解释:一个最长的回文串为 "lc" + "gg" + "cl" = "lcggcl" ,长度为 6 。
"clgglc" 是另一个可以得到的最长回文串。

示例 2:

输入:words = ["ab","ty","yt","lc","cl","ab"]
输出:8
解释:最长回文串是 "ty" + "lc" + "cl" + "yt" = "tylcclyt" ,长度为 8 。
"lcyttycl" 是另一个可以得到的最长回文串。

示例 3:

输入:words = ["cc","ll","xx"]
输出:2
解释:最长回文串是 "cc" ,长度为 2 。
"ll" 是另一个可以得到的最长回文串。"xx" 也是。

说明:

  • 1 <= words.length <= 10^5
  • words[i].length == 2
  • words[i] 仅包含小写英文字母。

思路

有一个字符串数组 words,其元素字符个数为 2,求从中选择任意元素组成回文串的最大长度。

统计每个元素的出现次数,如果数组元素的两个字符不同,要组成回文只能左右对称,计算对称的元素对数 cnt,长度为 cnt * 4。如果元素的两个字符相同,它可以全部放到中间,为了使回文最长,当出现更长的相同字符元素时,可以将原来放中间的个数 centerCnt,放到对称的两边,centerCnt / 2 * 4

代码


/**
 * @date 2025-05-25 1:03
 */
public class LongestPalindrome2131 {

    public int longestPalindrome(String[] words) {
        Map<String, Integer> map = new HashMap<>();
        for (String word : words) {
            char a = word.charAt(0);
            char b = word.charAt(1);
            map.merge(a + String.valueOf(b), 1, Integer::sum);
        }
        int res = 0;
        int centerCnt = 0;
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            char a = key.charAt(0);
            char b = key.charAt(1);
            if (a == b) {
                Integer cnt = entry.getValue();
                if (cnt % 2 == 1) {
                    res += centerCnt / 2 * 4;
                    centerCnt = cnt;
                } else {
                    res += cnt / 2 * 4;
                }
            } else {
                int cnt = Math.min(entry.getValue(), map.getOrDefault(b + String.valueOf(a), 0));
                res += cnt * 4;
            }
            entry.setValue(0);
        }
        return res + centerCnt * 2;
    }

}

性能

3362.零数组变换III

目标

给你一个长度为 n 的整数数组 nums 和一个二维数组 queries ,其中 queries[i] = [li, ri] 。

每一个 queries[i] 表示对于 nums 的以下操作:

  • 将 nums 中下标在范围 [li, ri] 之间的每一个元素 最多 减少 1 。
  • 坐标范围内每一个元素减少的值相互 独立 。

零数组 指的是一个数组里所有元素都等于 0 。

请你返回 最多 可以从 queries 中删除多少个元素,使得 queries 中剩下的元素仍然能将 nums 变为一个 零数组 。如果无法将 nums 变为一个 零数组 ,返回 -1 。

示例 1:

输入:nums = [2,0,2], queries = [[0,2],[0,2],[1,1]]
输出:1
解释:
删除 queries[2] 后,nums 仍然可以变为零数组。
对于 queries[0] ,将 nums[0] 和 nums[2] 减少 1 ,将 nums[1] 减少 0 。
对于 queries[1] ,将 nums[0] 和 nums[2] 减少 1 ,将 nums[1] 减少 0 。

示例 2:

输入:nums = [1,1,1,1], queries = [[1,3],[0,2],[1,3],[1,2]]
输出:2
解释:
可以删除 queries[2] 和 queries[3] 。

示例 3:

输入:nums = [1,2,3,4], queries = [[0,3]]
输出:-1
解释:
nums 无法通过 queries 变成零数组。

说明:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^5
  • 1 <= queries.length <= 10^5
  • queries[i].length == 2
  • 0 <= li <= ri < nums.length

思路

有一个长度为 n 的整数数组,每一次操作可以将给定范围内的任意元素减 1,返回最多可以删掉多少个操作,使得剩下的操作能够将数组中的所有元素变为 0

对于元素 nums[0] 要将其变为 0 必须要操作 nums[0] 次,且操作范围必须包括 0,因此可以按照操作范围的左端点排序,每次选择右端点最远即覆盖最广的操作区间。可以维护一个由大到小的优先队列,从操作中选出左端点小于等于当前下标的操作,将其右端点放入队列。从队列中取出右端点大于等于当前下标的操作,使用差分数组进行区间修改。

代码


/**
 * @date 2025-05-22 23:02
 */
public class MaxRemoval3362 {

    public int maxRemoval(int[] nums, int[][] queries) {
        PriorityQueue<Integer> q = new PriorityQueue<>((a, b) -> b - a);
        Arrays.sort(queries, (a, b) -> a[0] - b[0]);
        int n = nums.length;
        int[] diff = new int[n + 1];
        diff[0] = nums[0];
        for (int i = 1; i < n; i++) {
            diff[i] = nums[i] - nums[i - 1];
        }
        int num = 0;
        int k = 0;
        for (int i = 0; i < n; i++) {
            num += diff[i];
            while (k < queries.length && queries[k][0] <= i) {
                q.offer(queries[k++][1]);
            }
            while (!q.isEmpty() && q.peek() >= i && num > 0) {
                diff[q.poll() + 1]++;
                num--;
            }
            if (num > 0) {
                return -1;
            }
        }
        return q.size();
    }

}

性能

2900.最长相邻不相等子序列I

目标

给你一个下标从 0 开始的字符串数组 words ,和一个下标从 0 开始的 二进制 数组 groups ,两个数组长度都是 n 。

你需要从 words 中选出 最长子序列。如果对于序列中的任何两个连续串,二进制数组 groups 中它们的对应元素不同,则 words 的子序列是不同的。

正式来说,你需要从下标 [0, 1, ..., n - 1] 中选出一个 最长子序列 ,将这个子序列记作长度为 k 的 [i0, i1, ..., ik - 1] ,对于所有满足 0 <= j < k - 1 的 j 都有 groups[ij] != groups[ij + 1] 。

请你返回一个字符串数组,它是下标子序列 依次 对应 words 数组中的字符串连接形成的字符串数组。如果有多个答案,返回 任意 一个。

注意:words 中的元素是不同的 。

示例 1:

输入:words = ["e","a","b"], groups = [0,0,1]
输出:["e","b"]
解释:一个可行的子序列是 [0,2] ,因为 groups[0] != groups[2] 。
所以一个可行的答案是 [words[0],words[2]] = ["e","b"] 。
另一个可行的子序列是 [1,2] ,因为 groups[1] != groups[2] 。
得到答案为 [words[1],words[2]] = ["a","b"] 。
这也是一个可行的答案。
符合题意的最长子序列的长度为 2 。

示例 2:

输入:words = ["a","b","c","d"], groups = [1,0,1,1]
输出:["a","b","c"]
解释:一个可行的子序列为 [0,1,2] 因为 groups[0] != groups[1] 且 groups[1] != groups[2] 。
所以一个可行的答案是 [words[0],words[1],words[2]] = ["a","b","c"] 。
另一个可行的子序列为 [0,1,3] 因为 groups[0] != groups[1] 且 groups[1] != groups[3] 。
得到答案为 [words[0],words[1],words[3]] = ["a","b","d"] 。
这也是一个可行的答案。
符合题意的最长子序列的长度为 3 。

说明:

  • 1 <= n == words.length == groups.length <= 100
  • 1 <= words[i].length <= 10
  • groups[i] 是 0 或 1。
  • words 中的字符串 互不相同 。
  • words[i] 只包含小写英文字母。

思路

有一个长度为 n 的字符串数组 words,其中的字符串互不相同,同时它对应一个相同长度的二进制数组 groups,从二进制数组中找出一个相邻元素不同的最长子序列,并返回其在 words 中对应的子序列。

如何找相邻元素不同的最长子序列?只需选择所有数组元素,然后将相邻元素相同的元素删掉即可。

代码


/**
 * @date 2025-05-15 8:51
 */
public class GetLongestSubsequence2900 {

    public List<String> getLongestSubsequence(String[] words, int[] groups) {
        List<String> res = new ArrayList<>();
        int prev = 0;
        int n = words.length;
        res.add(words[0]);
        for (int i = 1; i < n; i++) {
            if (groups[prev] != groups[i]){
                res.add(words[i]);
                prev = i;
            }
        }
        return res;
    }
}

性能

2918.数组的最小相等和

目标

给你两个由正整数和 0 组成的数组 nums1 和 nums2 。

你必须将两个数组中的 所有 0 替换为 严格 正整数,并且满足两个数组中所有元素的和 相等 。

返回 最小 相等和 ,如果无法使两数组相等,则返回 -1 。

示例 1:

输入:nums1 = [3,2,0,1,0], nums2 = [6,5,0]
输出:12
解释:可以按下述方式替换数组中的 0 :
- 用 2 和 4 替换 nums1 中的两个 0 。得到 nums1 = [3,2,2,1,4] 。
- 用 1 替换 nums2 中的一个 0 。得到 nums2 = [6,5,1] 。
两个数组的元素和相等,都等于 12 。可以证明这是可以获得的最小相等和。

示例 2:

输入:nums1 = [2,0,2,0], nums2 = [1,4]
输出:-1
解释:无法使两个数组的和相等。

说明:

  • 1 <= nums1.length, nums2.length <= 10^5
  • 0 <= nums1[i], nums2[i] <= 10^6

思路

有两个非负数组,将其中的 0 替换为正整数并且使两个数组的所有元素和相等,求和的最小值。

判断两个数组中是否存在 0,如果不存在零,判断和是否相等,如果不相等返回 -1

如果数组中存在零,必须要将其改为正整数,为了使和最小,应改为 1

如果其中一个数组存在零,判断 修改后 数组的元素和是否大于另一数组的元素和,如果大于则无法使数组和相等返回 -1

如果两个数组都存在 0,取这两个数组修改后的最大和即可。

代码


/**
 * @date 2025-05-10 20:50
 */
public class MinSum2918 {

    public long minSum(int[] nums1, int[] nums2) {
        long sum1 = 0, sum2 = 0;
        int cnt1 = 0, cnt2 = 0;
        for (int num : nums1) {
            sum1 += num;
            cnt1 += num == 0 ? 1 : 0;
        }
        for (int num : nums2) {
            sum2 += num;
            cnt2 += num == 0 ? 1 : 0;
        }
        if (cnt1 == 0 && cnt2 == 0) {
            return sum1 == sum2 ? sum1 : -1;
        } else if (cnt1 != 0 && cnt2 == 0) {
            return sum1 + cnt1 <= sum2 ? sum2 : -1;
        } else if (cnt1 == 0) {
            return sum1 >= sum2 + cnt2 ? sum1 : -1;
        } else {
            return Math.max(sum1 + cnt1, sum2 + cnt2);
        }
    }

}

性能