1385.两个数组间的距离值

目标

给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。

「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足 |arr1[i]-arr2[j]| <= d 。

示例 1:

输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
输出:2
解释:
对于 arr1[0]=4 我们有:
|4-10|=6 > d=2 
|4-9|=5 > d=2 
|4-1|=3 > d=2 
|4-8|=4 > d=2 
所以 arr1[0]=4 符合距离要求

对于 arr1[1]=5 我们有:
|5-10|=5 > d=2 
|5-9|=4 > d=2 
|5-1|=4 > d=2 
|5-8|=3 > d=2
所以 arr1[1]=5 也符合距离要求

对于 arr1[2]=8 我们有:
|8-10|=2 <= d=2
|8-9|=1 <= d=2
|8-1|=7 > d=2
|8-8|=0 <= d=2
存在距离小于等于 2 的情况,不符合距离要求 

故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2

示例 2:

输入:arr1 = [1,4,2,3], arr2 = [-4,-3,6,10,20,30], d = 3
输出:2

示例 3:

输入:arr1 = [2,1,100,3], arr2 = [-5,-2,10,-3,7], d = 6
输出:1

说明:

  • 1 <= arr1.length, arr2.length <= 500
  • -10^3 <= arr1[i], arr2[j] <= 10^3
  • 0 <= d <= 100

思路

有两个数组 arr1 与 arr2,定义两个数组间的距离值为 arr1 中满足条件的元素个数,对于任意的 arr1[i] 需满足 |arr1[i] - arr2[j]| > d,返回两个数组的距离值。

排序 arr2,枚举 arr1,在 arr2 中二分查找第一个大于 arr1[i] 的下标 j,判断 arr1[i] 与 arr2[j - 1] 和 arr[j] 的距离是否满足要求即可。

代码


/**
 * @date 2026-11-05 10:48
 */
public class FindTheDistanceValue1385 {

    public int findTheDistanceValue(int[] arr1, int[] arr2, int d) {
        int n = arr2.length;
        Arrays.sort(arr2);
        int res = 0;
        for (int num : arr1) {
            int i = bs(num, arr2);
            if (i == n && Math.abs(num - arr2[i - 1]) > d) {
                res++;
            } else if (i > 0 && Math.abs(num - arr2[i - 1]) > d && Math.abs(num - arr2[i]) > d) {
                res++;
            } else if (i == 0 && Math.abs(num - arr2[i]) > d) {
                res++;
            }
        }
        return res;
    }

    public int bs(int target, int[] arr2) {
        int l = 0;
        int r = arr2.length - 1;
        int m = l + (r - l) / 2;
        while (l <= r) {
            if (target <= arr2[m]) {
                r = m - 1;
            } else {
                l = m + 1;
            }
            m = l + (r - l) / 2;
        }
        return l;
    }
}

性能

3882.网格图中最小异或路径

目标

给你一个大小为 m * n 的二维整数数组 grid。

你从 左上角 的单元格 (0, 0) 出发,想要到达 右下角 的单元格 (m - 1, n - 1)。

在每一步中,你 可以 向右或向下 移动。

路径的 代价 定义为该路径上所有单元格(包括 起点和终点)的值的 按位异或。

返回从 (0, 0) 到 (m - 1, n - 1) 的所有有效路径中 最小 的可能异或值。

示例 1:

输入: grid = [[1,2],[3,4]]
输出: 6
解释:
有两条有效路径:
(0, 0) → (0, 1) → (1, 1),异或值为:1 XOR 2 XOR 4 = 7
(0, 0) → (1, 0) → (1, 1),异或值为:1 XOR 3 XOR 4 = 6
所有有效路径中的最小异或值为 6。

示例 2:

输入: grid = [[6,7],[5,8]]
输出: 9
解释:
有两条有效路径:
(0, 0) → (0, 1) → (1, 1),异或值为:6 XOR 7 XOR 8 = 9
(0, 0) → (1, 0) → (1, 1),异或值为:6 XOR 5 XOR 8 = 11
所有有效路径中的最小异或值为 9。

示例 3:

输入: grid = [[2,7,5]]
输出: 0
解释:
只有一条有效路径:
(0, 0) → (0, 1) → (0, 2),异或值为:2 XOR 7 XOR 5 = 0
这条路径的异或值为 0,这是可能达到的最小值。

说明:

  • 1 <= m == grid.length <= 1000
  • 1 <= n == grid[i].length <= 1000
  • m * n <= 1000
  • 0 <= grid[i][j] <= 1023​

思路

有一个 m x n 的网格,允许向下或向右移动,求从左上到右下的路径上元素异或和的最小值。

注意到 m * n <= 1000,可以记忆路径到达 (i,j) 时的异或和,如果该状态已经访问过则跳过。

代码


/**
 * @date 2026-10-28 9:18
 */
public class MinCost3882 {

    public int minCost(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        boolean[][][] mem = new boolean[m][n][1024];
        dfs(0, 0, 0, grid, mem);
        for (int i = 0; i < 1024; i++) {
            if (mem[m - 1][n - 1][i]) {
                return i;
            }
        }
        return -1;
    }

    public void dfs(int i, int j, int xor, int[][] grid, boolean[][][] mem) {
        int m = grid.length;
        int n = grid[0].length;
        if (i >= m || j >= n) {
            return;
        }
        xor ^= grid[i][j];
        if (mem[i][j][xor]) {
            return;
        }
        mem[i][j][xor] = true;
        dfs(i + 1, j, xor, grid, mem);
        dfs(i, j + 1, xor, grid, mem);
    }

}

性能

3963.构造恰好一条路径的网格

目标

给你两个整数 m 和 n,分别表示网格的行数和列数。

请你构造 任意 一个只包含字符 '.' 和 '#' 的 m x n 网格,其中:

  • '.' 表示空单元格。
  • '#' 表示障碍物单元格。

有效路径 是满足以下条件的空单元格序列:

  • 从左上角单元格 (0, 0) 开始。
  • 在右下角单元格 (m - 1, n - 1) 结束。
  • 只能向:
    • 右移动,从 (i, j) 到 (i, j + 1),或者
    • 下移动,从 (i, j) 到 (i + 1, j)。

返回任意一个从左上角到右下角 恰好只有一条有效路径 的网格。

示例 1:

输入: m = 2, n = 3
输出: ["..#","#.."]
解释:
唯一的有效路径是:(0,0) → (0,1) → (1,1) → (1,2)

示例 2:

输入: m = 3, n = 3
输出: ["..#","#..","##."]
解释:
唯一的有效路径是:(0,0) → (0,1) → (1,1) → (1,2) → (2,2)

示例 3:

输入: m = 1, n = 4
输出: ["...."]
解释:
唯一的有效路径是:(0,0) → (0,1) → (0,2) → (0,3)

说明:

1 <= m, n <= 25

思路

构造一个 m x n 矩阵,要求从左上到右下 仅有一条 有效路径,使用 . 表示空格,# 表示有障碍物,返回任意一种构造即可。

直接的想法是第一行全空格,下面的行除了最后一列全是障碍物。

代码


/**
 * @date 2026-10-27 8:49
 */
public class CreateGrid3963 {

    public String[] createGrid_v1(int m, int n) {
        String[] res = new String[m];
        char[] chars = new char[n];
        Arrays.fill(chars, '#');
        chars[n - 1] = '.';
        String row = new String(chars);
        Arrays.fill(res, row);
        chars = new char[n];
        Arrays.fill(chars, '.');
        res[0] = new String(chars);
        return res;
    }

}

性能

3619.总价值可以被K整除的岛屿数目

目标

给你一个 m x n 的矩阵 grid 和一个正整数 k。一个 岛屿 是由 正 整数(表示陆地)组成的,并且陆地间 四周 连通(水平或垂直)。

一个岛屿的总价值是该岛屿中所有单元格的值之和。

返回总价值可以被 k 整除 的岛屿数量。

示例 1:

输入: grid = [[0,2,1,0,0],[0,5,0,0,5],[0,0,1,0,0],[0,1,4,7,0],[0,2,0,0,8]], k = 5
输出: 2
解释:
网格中包含四个岛屿。蓝色高亮显示的岛屿的总价值可以被 5 整除,而红色高亮显示的岛屿则不能。

示例 2:

输入: grid = [[3,0,3,0], [0,3,0,3], [3,0,3,0]], k = 3
输出: 6
解释:
网格中包含六个岛屿,每个岛屿的总价值都可以被 3 整除。

说明:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 1000
  • 1 <= m * n <= 10^5
  • 0 <= grid[i][j] <= 10^6
  • 1 <= k < = 10^6

思路

有一个 m x n 矩阵 grid 和一个正整数 k,定义正整数的格子为陆地,陆地四周相连组成的区域称为岛屿,岛屿的总价值为所有单元格的值之和。求矩阵中总价值能被 k 整除的岛屿数量。

枚举所有格子,以它为起点 dfs 矩阵,返回总价值,跳过已访问过的格子。

代码


/**
 * @date 2026-10-26 9:44
 */
public class CountIslands3619 {

    int[][] directions = new int[][]{{-1, 0}, {0, 1}, {1, 0}, {0, -1}};

    public int countIslands(int[][] grid, int k) {
        int m = grid.length;
        int n = grid[0].length;
        int res = 0;
        boolean[][] visited = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 0) {
                    visited[i][j] = true;
                } else if (!visited[i][j] && dfs(i, j, grid, visited) % k == 0) {
                    res++;
                }
            }
        }
        return res;
    }

    public long dfs(int i, int j, int[][] grid, boolean[][] visited) {
        int m = grid.length;
        int n = grid[0].length;
        visited[i][j] = true;
        long res = grid[i][j];
        for (int[] d : directions) {
            int ni = i + d[0];
            int nj = j + d[1];
            if (ni >= 0 && nj >= 0 && ni < m && nj < n && grid[i][j] > 0 && !visited[ni][nj]) {
                res += dfs(ni, nj, grid, visited);
            }
        }
        return res;
    }
}

性能

3644.排序排列

目标

给你一个长度为 n 的整数数组 nums,其中 nums 是范围 [0..n - 1] 内所有数字的一个 排列 。

你可以在满足条件 nums[i] AND nums[j] == k 的情况下交换下标 i 和 j 的元素,其中 AND 表示按位与操作,k 是一个非负整数。

返回可以使数组按 非递减 顺序排序的最大值 k(允许进行任意次这样的交换)。如果 nums 已经是有序的,返回 0。

排列 是数组所有元素的一种重新排列。

示例 1:

输入:nums = [0,3,2,1]
输出:1
解释:
选择 k = 1。交换 nums[1] = 3 和 nums[3] = 1,因为 nums[1] AND nums[3] == 1,从而得到一个排序后的排列:[0, 1, 2, 3]。

示例 2:

输入:nums = [0,1,3,2]
输出:2
解释:
选择 k = 2。交换 nums[2] = 3 和 nums[3] = 2,因为 nums[2] AND nums[3] == 2,从而得到一个排序后的排列:[0, 1, 2, 3]。

示例 3:

输入:nums = [3,2,1,0]
输出:0
解释:
只有当 k = 0 时,才能进行排序,因为没有更大的 k 能够满足 nums[i] AND nums[j] == k 的交换条件。

说明:

  • 1 <= n == nums.length <= 10^5
  • 0 <= nums[i] <= n - 1
  • nums 是从 0 到 n - 1 的一个排列。

思路

有一个 0 ~ n - 1 的排列 nums,如果 nums[i] & nums[j] == k,则允许交换元素 nums[i] 与 nums[j]。返回使得 nums 递增所允许的最大的 k,如果排列本来就是有序的返回 0。

任何数与 0 相与都是 0,如果想交换元素 nums[i] 和 nums[j],可以将元素 0 作为中间元素来交换,一定有解。

所有不在位置上的元素都是需要交换的,k 就是这些元素相与的结果,无论怎么排列,只是相与的顺序不同,结果是一样的。

代码


/**
 * @date 2026-10-23 9:06
 */
public class SortPermutation3644 {

    public int sortPermutation(int[] nums) {
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            if (nums[i] != i) {
                res &= nums[i];
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }

}

性能

3863.将一个字符串排序的最小操作次数

目标

给你一个由小写英文字母组成的字符串 s。

在一次操作中,你可以选择 s 的任意 子字符串(但 不能 是整个字符串),并将其按 非降序字母顺序 进行 排序。

返回使 s 按 非降序 排列所需的 最小 操作次数。如果无法做到,则返回 -1。

示例 1:

输入: s = "dog"
输出: 1
解释:
将子字符串 "og" 排序为 "go"。
现在,s = "dgo",已按升序排列。因此,答案是 1。

示例 2:

输入: s = "card"
输出: 2
解释:
将子字符串 "car" 排序为 "acr",得到 s = "acrd"。
将子字符串 "rd" 排序为 "dr",得到 s = "acdr",已按升序排列。因此,答案是 2。

示例 3:

输入: s = "gf"
输出: -1
解释:
在给定提示下,无法对 s 进行排序。因此,答案是 -1。

说明:

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

思路

有一个字符串 s,每次操作可以将其子串(不能是整个字符串)按非递减排序,求使整个字符串按非递减排列所需的最小操作次数,如果无法实现返回 -1。

分类讨论:

  • 如果 s 本身非递减,操作次数为 0。
  • 否则,找出最大字符 max 与 最小字符 min:
    • 如果 s[0] == min || s[n - 1] == max,只需将剩余部分排序即可,操作次数为 1。
    • 如果 s[0] == max && s[n - 1] == min:
    • 如果字符串中有多余的 min 或者 max,可以直接排序前缀 s[0, n - 2] 或后缀 s[1, n - 1] 使得最大或最小归位,然后再排序剩余部分,操作次数为 2。
    • 否则,需要先将 min 与 max 移出来:
      • 如果 n == 2,返回 -1。
      • 否则,将 min 与 前一个的元素交换,排序前缀 s[0, n - 2],然后再排序后缀 s[1, n - 1],操作次数为 3。
    • 其余情况,先排序前缀 s[0, n - 2] 再排序后缀 s[1, n - 1] 即可,操作次数为 2。

代码


/**
 * @date 2026-10-21 9:10
 */
public class MinOperations3863 {

    public int minOperations(String s) {
        int max = s.charAt(0);
        int min = max;
        char[] chars = s.toCharArray();
        int[] cnt = new int[26];
        int n = chars.length;
        boolean ordered = true;
        for (char c : chars) {
            if (c < max) {
                ordered = false;
            }
            cnt[c - 'a']++;
            max = Math.max(max, c);
            min = Math.min(min, c);
        }
        if (ordered) {
            return 0;
        } else if (max == s.charAt(n - 1) || min == s.charAt(0)) {
            return 1;
        } else if (max == s.charAt(0) && min == s.charAt(n - 1)) {
            if (n == 2) {
                return -1;
            } else if (cnt[min - 'a'] > 1 || cnt[max - 'a'] > 1) {
                return 2;
            } else {
                return 3;
            }
        }
        return 2;
    }

}

性能

2089.找出数组排序后的目标下标

目标

给你一个下标从 0 开始的整数数组 nums 以及一个目标元素 target 。

目标下标 是一个满足 nums[i] == target 的下标 i 。

将 nums 按 非递减 顺序排序后,返回由 nums 中目标下标组成的列表。如果不存在目标下标,返回一个 空 列表。返回的列表必须按 递增 顺序排列。

示例 1:

输入:nums = [1,2,5,2,3], target = 2
输出:[1,2]
解释:排序后,nums 变为 [1,2,2,3,5] 。
满足 nums[i] == 2 的下标是 1 和 2 。

示例 2:

输入:nums = [1,2,5,2,3], target = 3
输出:[3]
解释:排序后,nums 变为 [1,2,2,3,5] 。
满足 nums[i] == 3 的下标是 3 。

示例 3:

输入:nums = [1,2,5,2,3], target = 5
输出:[4]
解释:排序后,nums 变为 [1,2,2,3,5] 。
满足 nums[i] == 5 的下标是 4 。

示例 4:

输入:nums = [1,2,5,2,3], target = 4
输出:[]
解释:nums 中不含值为 4 的元素。

说明:

  • 1 <= nums.length <= 100
  • 1 <= nums[i], target <= 100

思路

有一个下标从 0 开始的数组 nums,从中查找值为 target 的元素,返回其在排序后的数组中的下标,如果没有找到返回空。

直接的想法是先排序数组,然后遍历查找目标,将下标加入结果集,时间复杂度为 O(nlogn)。

本题只关心特定元素在数组中的位次,只需统计左右两端的元素数目即可算出下标。

代码


/**
 * @date 2026-10-19 8:48
 */
public class TargetIndices2089 {

    public List<Integer> targetIndices(int[] nums, int target) {
        int n = nums.length;
        int l = 0, r = 0;
        List<Integer> res = new ArrayList<>();
        for (int num : nums) {
            if (num < target) {
                l++;
            } else if (num > target) {
                r++;
            }
        }
        int length = n - l - r;
        for (int i = 0; i < length; i++) {
            res.add(l++);
        }
        return res;
    }
}

性能

2913.子数组不同元素数目的平方和I

目标

给你一个下标从 0 开始的整数数组 nums 。

定义 nums 一个子数组的 不同计数 值如下:

  • 令 nums[i..j] 表示 nums 中所有下标在 i 到 j 范围内的元素构成的子数组(满足 0 <= i <= j < nums.length ),那么我们称子数组 nums[i..j] 中不同值的数目为 nums[i..j] 的不同计数。

请你返回 nums 中所有子数组的 不同计数 的 平方 和。

子数组指的是一个数组里面一段连续 非空 的元素序列。

示例 1:

输入:nums = [1,2,1]
输出:15
解释:六个子数组分别为:
[1]: 1 个互不相同的元素。
[2]: 1 个互不相同的元素。
[1]: 1 个互不相同的元素。
[1,2]: 2 个互不相同的元素。
[2,1]: 2 个互不相同的元素。
[1,2,1]: 2 个互不相同的元素。
所有不同计数的平方和为 1^2 + 1^2 + 1^2 + 2^2 + 2^2 + 2^2 = 15 。

示例 2:

输入:nums = [2,2]
输出:3
解释:三个子数组分别为:
[2]: 1 个互不相同的元素。
[2]: 1 个互不相同的元素。
[2,2]: 1 个互不相同的元素。
所有不同计数的平方和为 1^2 + 1^2 + 1^2 = 3 。

说明:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100

思路

定义子数组中不同元素值的个数为子数组计数,返回数组所有子数组计数的平方和。

直接的想法是枚举所有子数组,然后将子数组中的元素放入哈希表,累加子数组计数的平方,时间复杂度为 O(n^2)。

代码


/**
 * @date 2026-10-15 9:01
 */
public class SumCounts2913 {

    public int sumCounts_v2(List<Integer> nums) {
        int n = nums.size();
        int res = 0;
        for (int i = 0; i < n; i++) {
            Set<Integer> set = new HashSet<>();
            for (int j = i; j < n; j++) {
                set.add(nums.get(j));
                res += set.size() * set.size();
            }
        }
        return res;
    }

}

性能

3723.数位平方和的最大值

目标

给你两个 正 整数 num 和 sum。

如果一个正整数 n 满足以下两个条件,则称其为 好整数 :

  • n 的位数 恰好 为 num。
  • n 的各位数字之和 恰好 为 sum。

一个 好整数 n 的 分数 定义为 n 的各位数字的平方和。

返回一个 字符串 ,表示能获得 最大分数 的 好整数 n。如果有多个可能的整数,返回 最大 的那个。如果不存在这样的整数,返回一个空字符串。

示例 1:

输入: num = 2, sum = 3
输出: "30"
解释:
有 3 个好整数:12、21 和 30。
12 的分数是 12 + 22 = 5。
21 的分数是 22 + 12 = 5。
30 的分数是 32 + 02 = 9。
最大分数是 9,由好整数 30 获得。因此,答案是 "30"。

示例 2:

输入: num = 2, sum = 17
输出: "98"
解释:
有两个好整数:89 和 98。
89 的分数是 82 + 92 = 145。
98 的分数是 92 + 82 = 145。
最大分数是 145。获得此分数的最大好整数是 98。因此,答案是 "98"。

示例 3:

输入: num = 1, sum = 10
输出: ""
解释:
不存在恰好有 1 位数字且各位数字之和为 10 的整数。因此,答案是 ""。

说明:

  • 1 <= num <= 2 * 10^5
  • 1 <= sum <= 2 * 10^6

思路

定义数位长度为 num,数位之和为 sum 的整数为好整数,好整数的分数定义为各位数字的平方和,返回分数最大的好整数,如果分数相同则返回整数值最大的,如果不存在返回空串。

贪心策略就是取当前位所能取的最大值,余下的补 0。

代码


/**
 * @date 2026-10-14 9:21
 */
public class MaxSumOfSquares3723 {

    public String maxSumOfSquares(int num, int sum) {
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            int d = Math.min(sum, 9);
            sum -= d;
            num--;
            sb.append(d);
        }
        return sum == 0 ? sb.toString() : "";
    }

}

性能

3727.最大交替平方和

目标

给你一个整数数组 nums。你可以以任意顺序 重新排列元素 。

数组 arr 的 交替得分 定义为:

  • score = arr[0]^2 - arr[1]^2 + arr[2]^2 - arr[3]^2 + ...

在对 nums 重新排列后,返回其 最大可能的交替得分。

示例 1:

输入: nums = [1,2,3]
输出: 12
解释:
nums 的一种可行重排为 [2,1,3],该排列在所有可能重排中给出了最大交替得分。
交替得分计算如下:
score = 22 - 12 + 32 = 4 - 1 + 9 = 12

示例 2:

输入: nums = [1,-1,2,-2,3,-3]
输出: 16
解释:
nums 的一种可行重排为 [-3,-1,-2,1,3,2],该排列在所有可能重排中给出了最大交替得分。
交替得分计算如下:
score = (-3)^2 - (-1)^2 + (-2)^2 - (1)^2 + (3)^2 - (2)^2 = 9 - 1 + 4 - 1 + 9 - 4 = 16

说明:

  • 1 <= nums.length <= 10^5
  • -4 10^4 <= nums[i] <= 4 10^4

思路

有一个下标从 0 开始的整数数组 nums,数组的交替得分定义为 (nums[0]^2 - nums[1]^2) + (nums[2]^2 - nums[3]^2) + …… ,允许对数组重新排序,返回最大可能的交替得分。

依题意,得分为偶数下标元素的平方减去奇数下标元素的平方,要使得分最大,只需奇数下标的平方值最小。将数组替换为元素的平方,然后排序数组,将前 n/2 个元素放在奇数下标即可。

代码


/**
 * @date 2026-10-13 10:20
 */
public class MaxAlternatingSum3727 {

    public long maxAlternatingSum(int[] nums) {
        long res = 0L;
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            nums[i] = nums[i] * nums[i];
        }
        Arrays.sort(nums);
        for (int i = 0; i < n; i++) {
            if (i < n / 2) {
                res -= nums[i];
            } else {
                res += nums[i];
            }
        }
        return res;
    }
}

性能