3443.K次修改后的最大曼哈顿距离

目标

给你一个由字符 'N'、'S'、'E' 和 'W' 组成的字符串 s,其中 s[i] 表示在无限网格中的移动操作:

  • 'N':向北移动 1 个单位。
  • 'S':向南移动 1 个单位。
  • 'E':向东移动 1 个单位。
  • 'W':向西移动 1 个单位。

初始时,你位于原点 (0, 0)。你 最多 可以修改 k 个字符为任意四个方向之一。

请找出在 按顺序 执行所有移动操作过程中的 任意时刻 ,所能达到的离原点的 最大曼哈顿距离 。

曼哈顿距离 定义为两个坐标点 (xi, yi) 和 (xj, yj) 的横向距离绝对值与纵向距离绝对值之和,即 |xi - xj| + |yi - yj|。

示例 1:

输入:s = "NWSE", k = 1
输出:3
解释:
将 s[2] 从 'S' 改为 'N' ,字符串 s 变为 "NWNE" 。
移动操作 位置 (x, y) 曼哈顿距离 最大值
s[0] == 'N' (0, 1) 0 + 1 = 1 1
s[1] == 'W' (-1, 1) 1 + 1 = 2 2
s[2] == 'N' (-1, 2) 1 + 2 = 3 3
s[3] == 'E' (0, 2) 0 + 2 = 2 3
执行移动操作过程中,距离原点的最大曼哈顿距离是 3 。

示例 2:

输入:s = "NSWWEW", k = 3
输出:6
解释:
将 s[1] 从 'S' 改为 'N' ,将 s[4] 从 'E' 改为 'W' 。字符串 s 变为 "NNWWWW" 。
执行移动操作过程中,距离原点的最大曼哈顿距离是 6 。

说明:

  • 1 <= s.length <= 10^5
  • 0 <= k <= s.length
  • s 仅由 'N'、'S'、'E' 和 'W' 。

思路

从原点 (0, 0) 出发,根据操作序列 s 朝四个方向移动,最多可以修改序列中 k 个方向,求能够到达的距离原点的最大曼哈顿距离。

遍历过程中记录距离变小的次数,每修改一次可以使得最大距离加 2

网友指出每走一步可能增大的距离最多为 2 * k,但是不能超过往一个方向一直走的情况,即当前走的步数 i + 1

代码


/**
 * @date 2025-06-20 21:47
 */
public class MaxDistance3443 {

    private static final Map<Character, int[]> MAP = new HashMap<>();

    static {
        MAP.put('N', new int[]{0, 1});
        MAP.put('S', new int[]{0, -1});
        MAP.put('E', new int[]{1, 0});
        MAP.put('W', new int[]{-1, 0});
    }

    public int maxDistance(String s, int k) {
        int res = 0;
        int d = 0;
        int prev = 0;
        int backCnt = 0;
        int[] curPos = new int[]{0, 0};
        char[] move = s.toCharArray();
        for (char dir : move) {
            prev = d;
            int[] delta = MAP.get(dir);
            int dx = delta[0];
            int dy = delta[1];
            curPos[0] += dx;
            curPos[1] += dy;
            d = Math.abs(curPos[0]) + Math.abs(curPos[1]);
            if (prev > d) {
                backCnt++;
            }
            res = Math.max(res, d + Math.min(backCnt, k) * 2);
        }
        return res;
    }
}

性能

2294.划分数组使最大差为K

目标

给你一个整数数组 nums 和一个整数 k 。你可以将 nums 划分成一个或多个 子序列 ,使 nums 中的每个元素都 恰好 出现在一个子序列中。

在满足每个子序列中最大值和最小值之间的差值最多为 k 的前提下,返回需要划分的 最少 子序列数目。

子序列 本质是一个序列,可以通过删除另一个序列中的某些元素(或者不删除)但不改变剩下元素的顺序得到。

示例 1:

输入:nums = [3,6,1,2,5], k = 2
输出:2
解释:
可以将 nums 划分为两个子序列 [3,1,2] 和 [6,5] 。
第一个子序列中最大值和最小值的差值是 3 - 1 = 2 。
第二个子序列中最大值和最小值的差值是 6 - 5 = 1 。
由于创建了两个子序列,返回 2 。可以证明需要划分的最少子序列数目就是 2 。

示例 2:

输入:nums = [1,2,3], k = 1
输出:2
解释:
可以将 nums 划分为两个子序列 [1,2] 和 [3] 。
第一个子序列中最大值和最小值的差值是 2 - 1 = 1 。
第二个子序列中最大值和最小值的差值是 3 - 3 = 0 。
由于创建了两个子序列,返回 2 。注意,另一种最优解法是将 nums 划分成子序列 [1] 和 [2,3] 。

示例 3:

输入:nums = [2,2,4,5], k = 0
输出:3
解释:
可以将 nums 划分为三个子序列 [2,2]、[4] 和 [5] 。
第一个子序列中最大值和最小值的差值是 2 - 2 = 0 。
第二个子序列中最大值和最小值的差值是 4 - 4 = 0 。
第三个子序列中最大值和最小值的差值是 5 - 5 = 0 。
由于创建了三个子序列,返回 3 。可以证明需要划分的最少子序列数目就是 3 。

说明:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^5
  • 0 <= k <= 10^5

思路

将数组划分为子序列,要求子序列中最大元素与最小元素的差不超过 k,并且相同的元素只能被划分到同一个子序列中,求划分的最少子序列项目。

将数组排序然后遍历,记录当前划分的最小元素,尽可能地将符合条件的元素都划分到一起,如果差值超过 k 则一定需要划分,更新最小元素。

代码


/**
 * @date 2025-06-19 9:00
 */
public class PartitionArray2294 {

    public int partitionArray(int[] nums, int k) {
        Arrays.sort(nums);
        int res = 1;
        int min = nums[0];
        for (int num : nums) {
            if (num - min <= k) {
                continue;
            }
            res++;
            min = num;
        }
        return res;
    }
}

性能

2966.划分数组并满足最大差限制

目标

给你一个长度为 n 的整数数组 nums,以及一个正整数 k 。

将这个数组划分为 n / 3 个长度为 3 的子数组,并满足以下条件:

  • 子数组中 任意 两个元素的差必须 小于或等于 k 。

返回一个 二维数组 ,包含所有的子数组。如果不可能满足条件,就返回一个空数组。如果有多个答案,返回 任意一个 即可。

示例 1:

输入:nums = [1,3,4,8,7,9,3,5,1], k = 2
输出:[[1,1,3],[3,4,5],[7,8,9]]
解释:
每个数组中任何两个元素之间的差小于或等于 2。

示例 2:

输入:nums = [2,4,2,2,5,2], k = 2
输出:[]
解释:
将 nums 划分为 2 个长度为 3 的数组的不同方式有:
[[2,2,2],[2,4,5]] (及其排列)
[[2,2,4],[2,2,5]] (及其排列)
因为有四个 2,所以无论我们如何划分,都会有一个包含元素 2 和 5 的数组。因为 5 - 2 = 3 > k,条件无法被满足,所以没有合法的划分。

示例 3:

输入:nums = [4,2,9,8,2,12,7,12,10,5,8,5,5,7,9,2,5,11], k = 14
输出:[[2,2,12],[4,8,5],[5,9,7],[7,8,5],[5,9,10],[11,12,2]]
解释:
每个数组中任何两个元素之间的差小于或等于 14。

说明:

  • n == nums.length
  • 1 <= n <= 10^5
  • n 是 3 的倍数
  • 1 <= nums[i] <= 10^5
  • 1 <= k <= 10^5

思路

将数组划分为 n / 3 个长度为 3 的数组,使得子数组内元素的差值不超过 k。如果有多个答案返回任意一个即可,如果满足条件的子数组不够 n / 3,返回空数组。

注意将原数组划分为 n / 3 个数组,先从数组中取 3 个元素,再从剩余元素中取 3 个 …… 。并不是取不重复的长度为 3 的子数组。

排序后每三个元素一组判断即可,因为相邻元素的差值最小,只要有一组不满足条件,那么个数就不够,直接返回空数组。

代码


/**
 * @date 2025-06-18 0:08
 */
public class DivideArray2966 {

    public int[][] divideArray(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length;
        int[][] res = new int[n / 3][];
        for (int i = 1; i < n - 1; i += 3) {
            int cur = nums[i];
            int prev = nums[i - 1];
            int next = nums[i + 1];
            if (next - prev > k) {
                return new int[][]{};
            }
            res[i / 3] = new int[]{prev, cur, next};
        }
        return res;
    }

}

性能

3405.统计恰好有K个相等相邻元素的数组数目

目标

给你三个整数 n ,m ,k 。长度为 n 的 好数组 arr 定义如下:

  • arr 中每个元素都在 闭 区间 [1, m] 中。
  • 恰好 有 k 个下标 i (其中 1 <= i < n)满足 arr[i - 1] == arr[i] 。

请你返回可以构造出的 好数组 数目。

由于答案可能会很大,请你将它对 109 + 7 取余 后返回。

示例 1:

输入:n = 3, m = 2, k = 1
输出:4
解释:
总共有 4 个好数组,分别是 [1, 1, 2] ,[1, 2, 2] ,[2, 1, 1] 和 [2, 2, 1] 。
所以答案为 4 。

示例 2:

输入:n = 4, m = 2, k = 2
输出:6
解释:
好数组包括 [1, 1, 1, 2] ,[1, 1, 2, 2] ,[1, 2, 2, 2] ,[2, 1, 1, 1] ,[2, 2, 1, 1] 和 [2, 2, 2, 1] 。
所以答案为 6 。

示例 3:

输入:n = 5, m = 2, k = 0
输出:2
解释:
好数组包括 [1, 2, 1, 2, 1] 和 [2, 1, 2, 1, 2] 。
所以答案为 2 。

说明:

  • 1 <= n <= 10^5
  • 1 <= m <= 10^5
  • 0 <= k <= n - 1

思路

//todo

代码

性能

2016.增量元素之间的最大差值

目标

给你一个下标从 0 开始的整数数组 nums ,该数组的大小为 n ,请你计算 nums[j] - nums[i] 能求得的 最大差值 ,其中 0 <= i < j < n 且 nums[i] < nums[j] 。

返回 最大差值 。如果不存在满足要求的 i 和 j ,返回 -1 。

示例 1:

输入:nums = [7,1,5,4]
输出:4
解释:
最大差值出现在 i = 1 且 j = 2 时,nums[j] - nums[i] = 5 - 1 = 4 。
注意,尽管 i = 1 且 j = 0 时 ,nums[j] - nums[i] = 7 - 1 = 6 > 4 ,但 i > j 不满足题面要求,所以 6 不是有效的答案。

示例 2:

输入:nums = [9,4,3,2]
输出:-1
解释:
不存在同时满足 i < j 和 nums[i] < nums[j] 这两个条件的 i, j 组合。

示例 3:

输入:nums = [1,5,2,10]
输出:9
解释:
最大差值出现在 i = 0 且 j = 3 时,nums[j] - nums[i] = 10 - 1 = 9 。

说明:

  • n == nums.length
  • 2 <= n <= 1000
  • 1 <= nums[i] <= 10^9

思路

找出递增元素的最大差值。

遍历数组,找出当前的最小值,用当前值减去最小值即增量元素的差值,取其最大值即可。

代码


/**
 * @date 2025-06-16 0:17
 */
public class MaximumDifference2016 {

    public int maximumDifference(int[] nums) {
        int min = Integer.MAX_VALUE;
        int res = 0;
        for (int num : nums) {
            min = Math.min(min, num);
            res = Math.max(res, num - min);
        }
        return res == 0 ? -1 : res;
    }
}

性能

1432.改变一个整数能得到的最大差值

目标

给你一个整数 num 。你可以对它进行以下步骤共计 两次:

  • 选择一个数字 x (0 <= x <= 9).
  • 选择另一个数字 y (0 <= y <= 9) 。数字 y 可以等于 x 。
  • 将 num 中所有出现 x 的数位都用 y 替换。

令两次对 num 的操作得到的结果分别为 a 和 b 。

请你返回 a 和 b 的 最大差值 。

注意,新的整数(a 或 b)必须不能 含有前导 0,并且 非 0。

示例 1:

  • 输入:num = 555
  • 输出:888
  • 解释:第一次选择 x = 5 且 y = 9 ,并把得到的新数字保存在 a 中。
  • 第二次选择 x = 5 且 y = 1 ,并把得到的新数字保存在 b 中。
  • 现在,我们有 a = 999 和 b = 111 ,最大差值为 888

示例 2:

  • 输入:num = 9
  • 输出:8
  • 解释:第一次选择 x = 9 且 y = 9 ,并把得到的新数字保存在 a 中。
  • 第二次选择 x = 9 且 y = 1 ,并把得到的新数字保存在 b 中。
  • 现在,我们有 a = 9 和 b = 1 ,最大差值为 8

示例 3:

  • 输入:num = 123456
  • 输出:820000

示例 4:

  • 输入:num = 10000
  • 输出:80000

示例 5:

  • 输入:num = 9288
  • 输出:8700

说明:

  • 1 <= num <= 10^8

思路

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

2566.替换一个数字后的最大差值 的区别是不能有前导零。

最大值:找到第一个不是 9 的位置,将 num 中所有与之相同的数字替换为 9

最小值:找到第一个不是 1 的位置,如果是首位,将 num 中所有与之相同的数字替换为 1,否则替换为 0

代码


/**
 * @date 2025-06-15 18:15
 */
public class MaxDiff1432 {

    public int maxDiff(int num) {
        int a = num, b = num;
        String s = String.valueOf(num);
        int n = s.length();
        int m = (int) Math.pow(10, n - 1);
        int ar = '-', br = '-';
        boolean first = false;
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (ar == '-' && c != '9') {
                ar = c;
            }
            if (br == '-' && c > '1') {
                br = c;
                first = i == 0;
            }
            if (ar == c) {
                a += (9 - (c - '0')) * m;
            }
            if (br == c) {
                b -= ((c - '0') - (first ? 1 : 0)) * m;
            }
            m /= 10;
        }
        return a - b;
    }
}

性能

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;
    }

}

性能

3423.循环数组中相邻元素的最大差值

目标

给你一个 循环 数组 nums ,请你找出相邻元素之间的 最大 绝对差值。

注意:一个循环数组中,第一个元素和最后一个元素是相邻的。

示例 1:

输入:nums = [1,2,4]
输出:3
解释:
由于 nums 是循环的,nums[0] 和 nums[2] 是相邻的,它们之间的绝对差值是最大值 |4 - 1| = 3 。

示例 2:

输入:nums = [-5,-10,-5]
输出:5
解释:
相邻元素 nums[0] 和 nums[1] 之间的绝对差值为最大值 |-5 - (-10)| = 5 。

说明:

  • 2 <= nums.length <= 100
  • -100 <= nums[i] <= 100

思路

依题意模拟即可。

代码


/**
 * @date 2025-06-12 0:02
 */
public class MaxAdjacentDistance3423 {

    public int maxAdjacentDistance(int[] nums) {
        int res = 0;
        int n = nums.length;
        for (int i = 1; i <= n; i++) {
            res = Math.max(res, Math.abs(nums[i % n] - nums[i - 1]));
        }
        return res;
    }
}

性能

3445.奇偶频次间的最大差值II

目标

给你一个字符串 s 和一个整数 k 。请你找出 s 的子字符串 subs 中两个字符的出现频次之间的 最大 差值,freq[a] - freq[b] ,其中:

  • subs 的长度 至少 为 k 。
  • 字符 a 在 subs 中出现奇数次。
  • 字符 b 在 subs 中出现偶数次。

返回 最大 差值。

注意 ,subs 可以包含超过 2 个 互不相同 的字符。.

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

示例 1:

输入:s = "12233", k = 4
输出:-1
解释:
对于子字符串 "12233" ,'1' 的出现次数是 1 ,'3' 的出现次数是 2 。差值是 1 - 2 = -1 。

示例 2:

输入:s = "1122211", k = 3
输出:1
解释:
对于子字符串 "11222" ,'2' 的出现次数是 3 ,'1' 的出现次数是 2 。差值是 3 - 2 = 1 。

示例 3:

输入:s = "110", k = 3
输出:-1

说明:

  • 3 <= s.length <= 3 * 10^4
  • s 仅由数字 '0' 到 '4' 组成。
  • 输入保证至少存在一个子字符串是由一个出现奇数次的字符和一个出现偶数次的字符组成。
  • 1 <= k <= s.length

思路

有一个字符串 s 仅由 0 ~ 4 组成,求其长度至少为 k 的子串中,3442_奇偶频次间的最大差值I 的最大值。

// todo

代码

性能