2452.距离字典两次编辑以内的单词

目标

给你两个字符串数组 queries 和 dictionary 。数组中所有单词都只包含小写英文字母,且长度都相同。

一次 编辑 中,你可以从 queries 中选择一个单词,将任意一个字母修改成任何其他字母。从 queries 中找到所有满足以下条件的字符串:不超过 两次编辑内,字符串与 dictionary 中某个字符串相同。

请你返回 queries 中的单词列表,这些单词距离 dictionary 中的单词 编辑次数 不超过 两次 。单词返回的顺序需要与 queries 中原本顺序相同。

示例 1:

输入:queries = ["word","note","ants","wood"], dictionary = ["wood","joke","moat"]
输出:["word","note","wood"]
解释:
- 将 "word" 中的 'r' 换成 'o' ,得到 dictionary 中的单词 "wood" 。
- 将 "note" 中的 'n' 换成 'j' 且将 't' 换成 'k' ,得到 "joke" 。
- "ants" 需要超过 2 次编辑才能得到 dictionary 中的单词。
- "wood" 不需要修改(0 次编辑),就得到 dictionary 中相同的单词。
所以我们返回 ["word","note","wood"] 。

示例 2:

输入:queries = ["yes"], dictionary = ["not"]
输出:[]
解释:
"yes" 需要超过 2 次编辑才能得到 "not" 。
所以我们返回空数组。

说明:

  • 1 <= queries.length, dictionary.length <= 100
  • n == queries[i].length == dictionary[j].length
  • 1 <= n <= 100
  • 所有 queries[i] 和 dictionary[j] 都只包含小写英文字母。

思路

有两个字符串数组 queriesdictionary,它们的字符串长度都相等,每次编辑可以将 queries 中的任意字符串的任意一个字母改成其它字母,按 queries 顺序返回最多两次编辑能够与 dictionary 中某个字符串相同的 queries[i]

暴力枚举,比较 queries[i]dictionary 中的每个字符串不同的字符个数,如果小于等于 2 加入结果集合。

代码


/**
 * @date 2026-04-22 9:04
 */
public class TwoEditWords2452 {

    public List<String> twoEditWords_v2(String[] queries, String[] dictionary) {
        List<String> res = new ArrayList<>();
        for (String query : queries) {
            if (check(query, dictionary)) {
                res.add(query);
            }
        }
        return res;
    }

    public boolean check(String query, String[] dictionary) {
        for (String dict : dictionary) {
            int dis = 0;
            for (int k = 0; k < dict.length() && dis <= 2; k++) {
                if (query.charAt(k) != dict.charAt(k)) {
                    dis++;
                }
            }
            if (dis <= 2) {
                return true;
            }
        }
        return false;
    }

}

性能

1722.执行交换操作后的最小汉明距离

目标

给你两个整数数组 source 和 target ,长度都是 n 。还有一个数组 allowedSwaps ,其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 ai 和 bi(下标从 0 开始)的两个元素。注意,你可以按 任意 顺序 多次 交换一对特定下标指向的元素。

相同长度的两个数组 source 和 target 间的 汉明距离 是元素不同的下标数量。形式上,其值等于满足 source[i] != target[i] (下标从 0 开始)的下标 i(0 <= i <= n-1)的数量。

在对数组 source 执行 任意 数量的交换操作后,返回 source 和 target 间的 最小汉明距离 。

示例 1:

输入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
输出:1
解释:source 可以按下述方式转换:
- 交换下标 0 和 1 指向的元素:source = [2,1,3,4]
- 交换下标 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 间的汉明距离是 1 ,二者有 1 处元素不同,在下标 3 。

示例 2:

输入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
输出:2
解释:不能对 source 执行交换操作。
source 和 target 间的汉明距离是 2 ,二者有 2 处元素不同,在下标 1 和下标 2 。

示例 3:

输入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
输出:0

说明:

  • n == source.length == target.length
  • 1 <= n <= 10^5
  • 1 <= source[i], target[i] <= 10^5
  • 0 <= allowedSwaps.length <= 10^5
  • allowedSwaps[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi

思路

有两个长度相等的数组 sourcetarget,另有一个交换数组 allowedSwaps[i] = [ai, bi],表示可以交换 source[ai]source[bi]。求按照任意顺序交换任意次之后,sourcetarget 的最小汉明距离。汉明距离指满足 source[i] != target[i]i 的个数。

记录可以交换的连通元素的频次,然后比较对应位置上 target 的元素,最小的汉明距离就是对应不上的元素个数。

使用并查集记录连通分量,为每一个连通分量维护一个哈希表,记录元素出现的频次。枚举 target,找到所属的连通分量,将对应连通分量的元素频次 -1,如果最终频次小于 0 那么汉明距离 +1

代码


/**
 * @date 2026-04-21 9:12
 */
public class MinimumHammingDistance1722 {

    public class UnionFind {
        int[] fa;

        public UnionFind(int n) {
            this.fa = new int[n];
            Arrays.setAll(this.fa, i -> i);
        }

        public int find(int e) {
            if (e != fa[e]) {
                fa[e] = find(fa[e]);
            }
            return fa[e];
        }

        public void merge(int x, int y) {
            int a = find(x);
            int b = find(y);
            if (a < b) {
                fa[b] = a;
            } else if (a > b) {
                fa[a] = b;
            }
        }
    }

    public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
        int n = source.length;
        UnionFind uf = new UnionFind(n);
        for (int[] swap : allowedSwaps) {
            uf.merge(swap[0], swap[1]);
        }
        Map<Integer, Map<Integer, Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            int c = uf.find(i);
            map.putIfAbsent(c, new HashMap<>());
            map.get(c).merge(source[i], 1, Integer::sum);
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            int c = uf.find(i);
            map.get(c).merge(target[i], -1, Integer::sum);
            if (map.get(c).get(target[i]) < 0) {
                res++;
            }
        }
        return res;
    }

}

性能

1855.下标对中的最大距离

目标

给你两个 非递增 的整数数组 nums1 和 nums2 ,数组下标均 从 0 开始 计数。

下标对 (i, j) 中 0 <= i < nums1.length 且 0 <= j < nums2.length 。如果该下标对同时满足 i <= j 且 nums1[i] <= nums2[j] ,则称之为 有效 下标对,该下标对的 距离 为 j - i 。

返回所有 有效 下标对 (i, j) 中的 最大距离 。如果不存在有效下标对,返回 0 。

一个数组 arr ,如果每个 1 <= i < arr.length 均有 arr[i-1] >= arr[i] 成立,那么该数组是一个 非递增 数组。

示例 1:

输入:nums1 = [55,30,5,4,2], nums2 = [100,20,10,10,5]
输出:2
解释:有效下标对是 (0,0), (2,2), (2,3), (2,4), (3,3), (3,4) 和 (4,4) 。
最大距离是 2 ,对应下标对 (2,4) 。

示例 2:

输入:nums1 = [2,2,2], nums2 = [10,10,1]
输出:1
解释:有效下标对是 (0,0), (0,1) 和 (1,1) 。
最大距离是 1 ,对应下标对 (0,1) 。

示例 3:

输入:nums1 = [30,29,19,5], nums2 = [25,25,25,25,25]
输出:2
解释:有效下标对是 (2,2), (2,3), (2,4), (3,3) 和 (3,4) 。
最大距离是 2 ,对应下标对 (2,4) 。

说明:

  • 1 <= nums1.length <= 10^5
  • 1 <= nums2.length <= 10^5
  • 1 <= nums1[i], nums2[j] <= 10^5
  • nums1 和 nums2 都是 非递增 数组

思路

有两个非递增的整数数组 nums1nums2,返回有效下标对的最大距离,如果不存在有效下标对,返回 0。有效下标对 (i, j) 需要满足 i <= j && nums1[i] <= nums2[j]

二分查找 nums2 大于等于 nums1[i] 的最大下标,记录 j - i 的最大值。

代码


/**
 * @date 2026-04-19 23:44
 */
public class MaxDistance1855 {

    public int maxDistance(int[] nums1, int[] nums2) {
        int res = 0;
        for (int i = 0; i < nums1.length; i++) {
            int j = upperBound(nums2, i, nums1[i]);
            res = Math.max(res, j - i);
        }
        return res;
    }

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

性能

3761.镜像对之间最小绝对距离

目标

给你一个整数数组 nums。

镜像对 是指一对满足下述条件的下标 (i, j):

  • 0 <= i < j < nums.length,并且
  • reverse(nums[i]) == nums[j],其中 reverse(x) 表示将整数 x 的数字反转后形成的整数。反转后会忽略前导零,例如 reverse(120) = 21。

返回任意镜像对的下标之间的 最小绝对距离。下标 i 和 j 之间的绝对距离为 abs(i - j)。

如果不存在镜像对,返回 -1。

示例 1:

输入: nums = [12,21,45,33,54]
输出: 1
解释:
镜像对为:
(0, 1),因为 reverse(nums[0]) = reverse(12) = 21 = nums[1],绝对距离为 abs(0 - 1) = 1。
(2, 4),因为 reverse(nums[2]) = reverse(45) = 54 = nums[4],绝对距离为 abs(2 - 4) = 2。
所有镜像对中的最小绝对距离是 1。

示例 2:

输入: nums = [120,21]
输出: 1
解释:
只有一个镜像对 (0, 1),因为 reverse(nums[0]) = reverse(120) = 21 = nums[1]。
最小绝对距离是 1。

示例 3:

输入: nums = [21,120]
输出: -1
解释:
数组中不存在镜像对。

说明:

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

思路

有一个整数数组 nums,镜像对指满足条件的下标对 (i, j)0 <= i < j < nums.length && reverse(nums[i]) == nums[j]reverse(x)x 的十进制表示的顺序反转并且去掉前导零。返回镜像对的最小距离。

维护左边的镜像值,枚举右,判断已维护的镜像值中是否存在当前值,取距离的最小值即可。

代码


/**
 * @date 2026-04-17 8:49
 */
public class MinMirrorPairDistance3761 {

    public int minMirrorPairDistance(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            if (map.get(num) != null) {
                res = Math.min(res, i - map.get(num));
            }
            int mirror = 0;
            int base = 10;
            while (num > 0) {
                mirror = (num % 10) + mirror * base;
                num /= 10;
            }
            map.put(mirror, i);
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }

}

性能

3488.距离最小相等元素查询

目标

给你一个 环形 数组 nums 和一个数组 queries 。

对于每个查询 i ,你需要找到以下内容:

  • 数组 nums 中下标 queries[i] 处的元素与 任意 其他下标 j(满足 nums[j] == nums[queries[i]])之间的 最小 距离。如果不存在这样的下标 j,则该查询的结果为 -1 。

返回一个数组 answer,其大小与 queries 相同,其中 answer[i] 表示查询i的结果。

示例 1:

输入: nums = [1,3,1,4,1,3,2], queries = [0,3,5]
输出: [2,-1,3]
解释:
查询 0:下标 queries[0] = 0 处的元素为 nums[0] = 1 。最近的相同值下标为 2,距离为 2。
查询 1:下标 queries[1] = 3 处的元素为 nums[3] = 4 。不存在其他包含值 4 的下标,因此结果为 -1。
查询 2:下标 queries[2] = 5 处的元素为 nums[5] = 3 。最近的相同值下标为 1,距离为 3(沿着循环路径:5 -> 6 -> 0 -> 1)。

示例 2:

输入: nums = [1,2,3,4], queries = [0,1,2,3]
输出: [-1,-1,-1,-1]
解释:
数组 nums 中的每个值都是唯一的,因此没有下标与查询的元素值相同。所有查询的结果均为 -1。

说明:

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

思路

有一个环形数组 nums 和一个查询数组 queries,返回 元素值与 queries[i] 相同的其它元素之间的最小距离,如果不存在则结果为 -1,返回查询结果数组。

根据元素值分组,分组内记录下标。针对每个查询,二分查找元素在分组中的位置,比较前后下标的距离,如果分组只有一个元素,返回 -1。需要特殊处理首与尾的最近距离。

也可以分组后预处理每个位置的最近距离,统一计算前后的最近距离,查询时直接取结果即可。注意循环数组的距离处理,这里直接将超出的距离映射回了原数组下标,因此最近距离需要取 min(d, n - d),其中 d = abs(i - j) 表示下标 ij 的距离。

代码


/**
 * @date 2026-04-16 8:55
 */
public class SolveQueries3488 {

    public List<Integer> solveQueries_v1(int[] nums, int[] queries) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        int[] dis = new int[n];
        for (List<Integer> list : map.values()) {
            int size = list.size();
            for (int i = 0; i < size; i++) {
                int index = list.get(i);
                if (size >= 2) {
                    int prev = Math.abs(index - list.get((size + i - 1) % size));
                    int next = Math.abs(list.get((i + 1) % size) - index);
                    dis[index] = Math.min(Math.min(prev, n - prev), Math.min(next, n - next));
                } else {
                    dis[index] = -1;
                }
            }
        }
        List<Integer> res = new ArrayList<>(queries.length);
        for (int query : queries) {
            res.add(dis[query]);
        }
        return res;
    }

}

性能

3741.三个相等元素之间的最小距离II

目标

给你一个整数数组 nums。

如果满足 nums[i] == nums[j] == nums[k],且 (i, j, k) 是 3 个 不同 下标,那么三元组 (i, j, k) 被称为 有效三元组 。

有效三元组 的 距离 被定义为 abs(i - j) + abs(j - k) + abs(k - i),其中 abs(x) 表示 x 的 绝对值 。

返回一个整数,表示 有效三元组 的 最小 可能距离。如果不存在 有效三元组 ,返回 -1。

示例 1:

输入: nums = [1,2,1,1,3]
输出: 6
解释:
最小距离对应的有效三元组是 (0, 2, 3) 。
(0, 2, 3) 是一个有效三元组,因为 nums[0] == nums[2] == nums[3] == 1。它的距离为 abs(0 - 2) + abs(2 - 3) + abs(3 - 0) = 2 + 1 + 3 = 6。

示例 2:

输入: nums = [1,1,2,3,2,1,2]
输出: 8
解释:
最小距离对应的有效三元组是 (2, 4, 6) 。
(2, 4, 6) 是一个有效三元组,因为 nums[2] == nums[4] == nums[6] == 2。它的距离为 abs(2 - 4) + abs(4 - 6) + abs(6 - 2) = 2 + 2 + 4 = 8。

示例 3:

输入: nums = [1]
输出: -1
解释:
不存在有效三元组,因此答案为 -1。

说明:

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

思路

参考 3740.三个相等元素之间的最小距离I,数据范围变大了,当时写的就是 O(n) 的解法。

代码


/**
 * @date 2026-04-11 8:46
 */
public class MinimumDistance3740 {

    public int minimumDistance(int[] nums) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        int res = Integer.MAX_VALUE;
        for (List<Integer> list : map.values()) {
            int size = list.size();
            if (size < 3) {
                continue;
            }
            for (int i = 2; i < size; i++) {
                res = Math.min(res, list.get(i) * 2 - list.get(i - 2) * 2);
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }
}

性能

3653.区间乘法查询后的异或I

目标

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

对于每个查询,按以下步骤执行操作:

  • 设定 idx = li。
  • 当 idx <= ri 时:
    • 更新:nums[idx] = (nums[idx] * vi) % (10^9 + 7)
    • 将 idx += ki。

在处理完所有查询后,返回数组 nums 中所有元素的 按位异或 结果。

示例 1:

输入: nums = [1,1,1], queries = [[0,2,1,4]]
输出: 4
解释:
唯一的查询 [0, 2, 1, 4] 将下标 0 到下标 2 的每个元素乘以 4。
数组从 [1, 1, 1] 变为 [4, 4, 4]。
所有元素的异或为 4 ^ 4 ^ 4 = 4。

示例 2:

输入: nums = [2,3,1,5,4], queries = [[1,4,2,3],[0,2,1,2]]
输出: 31
解释:
第一个查询 [1, 4, 2, 3] 将下标 1 和 3 的元素乘以 3,数组变为 [2, 9, 1, 15, 4]。
第二个查询 [0, 2, 1, 2] 将下标 0、1 和 2 的元素乘以 2,数组变为 [4, 18, 2, 15, 4]。
所有元素的异或为 4 ^ 18 ^ 2 ^ 15 ^ 4 = 31。

说明:

  • 1 <= n == nums.length <= 10^3
  • 1 <= nums[i] <= 10^9
  • 1 <= q == queries.length <= 10^3
  • queries[i] = [li, ri, ki, vi]
  • 0 <= li <= ri < n
  • 1 <= ki <= n
  • 1 <= vi <= 10^5

思路

有一个长度为 n 的数组,对该数组执行 n 次查询,每次查询从 li 开始,对相距 ki 个位置上的元素执行 nums[idx] = (nums[idx] * vi) % (10^9 + 7) 直到下标 idx > ri。求处理完所有查询后 nums 中所有元素的 按位异或 结果。

查询次数最大为 10^3,每次查询区间也是 [0, 10^3 - 1],步长 ki ∈ [1, 10^3]

可以直接模拟。

代码


/**
 * @date 2026-04-08 8:56
 */
public class XorAfterQueries3653 {

    public int xorAfterQueries(int[] nums, int[][] queries) {
        int mod = 1000000007;
        for (int[] query : queries) {
            int li = query[0], ri = query[1], ki = query[2], vi = query[3];
            while (li <= ri) {
                nums[li] = (int) ((long) nums[li] * vi % mod);
                li += ki;
            }
        }
        int res = 0;
        for (int num : nums) {
            res ^= num;
        }
        return res;
    }

}

性能

2069.模拟行走机器人II

目标

给你一个在 XY 平面上的 width x height 的网格图,左下角 的格子为 (0, 0) ,右上角 的格子为 (width - 1, height - 1) 。网格图中相邻格子为四个基本方向之一("North","East","South" 和 "West")。一个机器人 初始 在格子 (0, 0) ,方向为 "East" 。

机器人可以根据指令移动指定的 步数 。每一步,它可以执行以下操作。

  1. 沿着当前方向尝试 往前一步 。
  2. 如果机器人下一步将到达的格子 超出了边界 ,机器人会 逆时针 转 90 度,然后再尝试往前一步。

如果机器人完成了指令要求的移动步数,它将停止移动并等待下一个指令。

请你实现 Robot 类:

  • Robot(int width, int height) 初始化一个 width x height 的网格图,机器人初始在 (0, 0) ,方向朝 "East" 。
  • void step(int num) 给机器人下达前进 num 步的指令。
  • int[] getPos() 返回机器人当前所处的格子位置,用一个长度为 2 的数组 [x, y] 表示。
  • String getDir() 返回当前机器人的朝向,为 "North" ,"East" ,"South" 或者 "West" 。

示例 1:

输入:
["Robot", "step", "step", "getPos", "getDir", "step", "step", "step", "getPos", "getDir"]
[[6, 3], [2], [2], [], [], [2], [1], [4], [], []]
输出:
[null, null, null, [4, 0], "East", null, null, null, [1, 2], "West"]

解释:
Robot robot = new Robot(6, 3); // 初始化网格图,机器人在 (0, 0) ,朝东。
robot.step(2);  // 机器人朝东移动 2 步,到达 (2, 0) ,并朝东。
robot.step(2);  // 机器人朝东移动 2 步,到达 (4, 0) ,并朝东。
robot.getPos(); // 返回 [4, 0]
robot.getDir(); // 返回 "East"
robot.step(2);  // 朝东移动 1 步到达 (5, 0) ,并朝东。
                // 下一步继续往东移动将出界,所以逆时针转变方向朝北。
                // 然后,往北移动 1 步到达 (5, 1) ,并朝北。
robot.step(1);  // 朝北移动 1 步到达 (5, 2) ,并朝 北 (不是朝西)。
robot.step(4);  // 下一步继续往北移动将出界,所以逆时针转变方向朝西。
                // 然后,移动 4 步到 (1, 2) ,并朝西。
robot.getPos(); // 返回 [1, 2]
robot.getDir(); // 返回 "West"

说明:

  • 2 <= width, height <= 100
  • 1 <= num <= 10^5
  • step ,getPos 和 getDir 总共 调用次数不超过 10^4 次。

思路

有一个 w x h 的网格图,左下格子的坐标为 (0, 0),初始时机器人朝向东方,机器人可以根据指令沿着当前朝向移动指定的步数,如果下一步到达了网格边界,则逆时针旋转 90° 继续尝试。实现 Robot 类,可以执行移动,以及获取机器人当前的位置、朝向。

暴力解法是模拟走每一步,如果越界则转向,直到剩余步数为 0。但是由于 step 调用次数最大为 10^4,所走步数是 int 类型,模拟走每一步会超时。

注意到机器人只能沿着网格的最外层绕圈,对周长 (w - 1 + h - 1)* 2 取模,只需模拟最后一圈即可。有一个出错点是回到原点的朝向,初始时朝向东方,回到原点时,朝向南方,需要特殊处理。

代码


/**
 * @date 2026-04-07 8:57
 */
public class Robot2069 {

    static class Robot {

        int[][] grid;
        int[][] directions = new int[][]{{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
        String[] res = new String[]{"South", "East", "North", "West"};
        int d = 1;
        int m;
        int n;
        int[] pos;

        public Robot(int width, int height) {
            m = height;
            n = width;
            grid = new int[m][n];
            pos = new int[]{0, 0};
        }

        public void step(int num) {
            int step = num % (m + n + m + n - 4);
            // 当位于原点,方向朝东时,再走 k 圈的方向应该朝南,如果不在原点的话,方向还是朝东
            if (step == 0 && d == 1 && pos[0] == 0 && pos[1] == 0) {
                d = 0;
            }
            while (step > 0) {
                while (pos[0] + directions[d][0] < 0
                        || pos[0] + directions[d][0] == n
                        || pos[1] + directions[d][1] < 0
                        || pos[1] + directions[d][1] == m
                ) {
                    d = ++d % 4;
                }
                pos[0] += directions[d][0];
                pos[1] += directions[d][1];
                step--;
            }
        }

        public int[] getPos() {
            return pos;
        }

        public String getDir() {
            return res[d];
        }
    }

}
/**
 * Your Robot object will be instantiated and called as such:
 * Robot obj = new Robot(width, height);
 * obj.step(num);
 * int[] param_2 = obj.getPos();
 * String param_3 = obj.getDir();
 */

性能

874.模拟行走机器人

目标

机器人在一个无限大小的 XY 网格平面上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令 commands :

  • -2 :向左转 90 度
  • -1 :向右转 90 度
  • 1 <= x <= 9 :向前移动 x 个单位长度

在网格上有一些格子被视为障碍物 obstacles 。第 i 个障碍物位于网格点 obstacles[i] = (xi, yi) 。

机器人无法走到障碍物上,它将会停留在障碍物的前一个网格方块上,并继续执行下一个命令。

返回机器人距离原点的 最大欧式距离 的 平方 。(即,如果距离为 5 ,则返回 25 )

注意:

  • 北方表示 +Y 方向。
  • 东方表示 +X 方向。
  • 南方表示 -Y 方向。
  • 西方表示 -X 方向。
  • 原点 [0,0] 可能会有障碍物。

示例 1:

输入:commands = [4,-1,3], obstacles = []
输出:25
解释:
机器人开始位于 (0, 0):
1. 向北移动 4 个单位,到达 (0, 4)
2. 右转
3. 向东移动 3 个单位,到达 (3, 4)
距离原点最远的是 (3, 4) ,距离为 3^2 + 4^2 = 25

示例 2:

输入:commands = [4,-1,4,-2,4], obstacles = [[2,4]]
输出:65
解释:机器人开始位于 (0, 0):
1. 向北移动 4 个单位,到达 (0, 4)
2. 右转
3. 向东移动 1 个单位,然后被位于 (2, 4) 的障碍物阻挡,机器人停在 (1, 4)
4. 左转
5. 向北走 4 个单位,到达 (1, 8)
距离原点最远的是 (1, 8) ,距离为 1^2 + 8^2 = 65

示例 3:

输入:commands = [6,-1,-1,6], obstacles = []
输出:36
解释:机器人开始位于 (0, 0):
1. 向北移动 6 个单位,到达 (0, 6).
2. 右转
3. 右转
4. 向南移动 6 个单位,到达 (0, 0).
机器人距离原点最远的点是 (0, 6),其距离的平方是 6^2 = 36 个单位。

说明:

  • 1 <= commands.length <= 10^4
  • commands[i] 的值可以取 -2、-1 或者是范围 [1, 9] 内的一个整数。
  • 0 <= obstacles.length <= 10^4
  • -3 10^4 <= xi, yi <= 3 10^4
  • 答案保证小于 2^31

思路

二维平面的原点 (0, 0) 有一个机器人,开始时机器人面向北方。同时平面上还有一些障碍物 obstacles[i] = (xi, yi)。机器人可以根据指令 commands[i] 进行转向(-2 表示左转 90°-1 表示右转 90°)或者向前移动对应数量的格子,如果碰到障碍物则在障碍物前停止,并继续执行下一条指令,返回机器人距离原点的最大欧式距离,坐标 (x, y) 距离原点的欧式距离为 x^2 + y^2

由于机器人可能会往回走,最远的欧式距离可能在之前达到。

根据题目的指令描述模拟该过程,按照逆时针方向初始化上左下右四个方向的 (dx, dy)。初始方向 d = 0,左转时 d = (d + 1) % 4,右转时 d = (d + 3) % 4。为了判断下一格子是否有障碍物,可以使用哈希表 (rowi, coli_Set)保存障碍物的坐标。

网友题解是将坐标压缩到一个 int 中保存了,哈希表使用的是 Set<Integer>。由于坐标存在负值,这里还根据题目范围加了一个偏移量。

代码


/**
 * @date 2026-04-07 10:43
 */
public class RobotSim874 {

    public int robotSim(int[] commands, int[][] obstacles) {
        Map<Integer, Set<Integer>> map = new HashMap<>();
        for (int[] o : obstacles) {
            map.putIfAbsent(o[0], new HashSet<>());
            map.get(o[0]).add(o[1]);
        }
        int res = 0;
        int[] pos = new int[]{0, 0};
        int[][] directions = new int[][]{{0, 1}, {-1, 0}, {0, -1}, {1, 0}};
        int d = 0;
        for (int command : commands) {
            if (command == -1) {
                d = (d + 3) % 4;
            } else if (command == -2) {
                d = (d + 1) % 4;
            } else {
                while (command > 0) {
                    Set<Integer> set = map.get(pos[0] + directions[d][0]);
                    if (set != null &&
                            set.contains(pos[1] + directions[d][1])) {
                        break;
                    }
                    pos[0] += directions[d][0];
                    pos[1] += directions[d][1];
                    command--;
                }
                res = Math.max(res, pos[0] * pos[0] + pos[1] * pos[1]);
            }
        }
        return res;
    }

}

性能

2087.网格图中机器人回家的最小代价

目标

给你一个 m x n 的网格图,其中 (0, 0) 是最左上角的格子,(m - 1, n - 1) 是最右下角的格子。给你一个整数数组 startPos ,startPos = [startrow, startcol] 表示 初始 有一个 机器人 在格子 (startrow, startcol) 处。同时给你一个整数数组 homePos ,homePos = [homerow, homecol] 表示机器人的 家 在格子 (homerow, homecol) 处。

机器人需要回家。每一步它可以往四个方向移动:上,下,左,右,同时机器人不能移出边界。每一步移动都有一定代价。再给你两个下标从 0 开始的额整数数组:长度为 m 的数组 rowCosts 和长度为 n 的数组 colCosts 。

  • 如果机器人往 上 或者往 下 移动到第 r 行 的格子,那么代价为 rowCosts[r] 。
  • 如果机器人往 左 或者往 右 移动到第 c 列 的格子,那么代价为 colCosts[c] 。

请你返回机器人回家需要的 最小总代价 。

示例 1:

输入:startPos = [1, 0], homePos = [2, 3], rowCosts = [5, 4, 3], colCosts = [8, 2, 6, 7]
输出:18
解释:一个最优路径为:
从 (1, 0) 开始
-> 往下走到 (2, 0) 。代价为 rowCosts[2] = 3 。
-> 往右走到 (2, 1) 。代价为 colCosts[1] = 2 。
-> 往右走到 (2, 2) 。代价为 colCosts[2] = 6 。
-> 往右走到 (2, 3) 。代价为 colCosts[3] = 7 。
总代价为 3 + 2 + 6 + 7 = 18

示例 2:

输入:startPos = [0, 0], homePos = [0, 0], rowCosts = [5], colCosts = [26]
输出:0
解释:机器人已经在家了,所以不需要移动。总代价为 0 。

说明:

  • m == rowCosts.length
  • n == colCosts.length
  • 1 <= m, n <= 10^5
  • 0 <= rowCosts[r], colCosts[c] <= 10^4
  • startPos.length == 2
  • homePos.length == 2
  • 0 <= startrow, homerow < m
  • 0 <= startcol, homecol < n

思路

有一个 m x n 矩阵,其中有一个机器人在坐标 startPos,机器人家的坐标是 homePos,机器人可以上下左右移动,从上/下移动到第 i 行的成本为 rowCosts[i],从左/右移动到第 j 列的成本为 colCosts[j],求机器人回家需要的最小总代价。

根据题意代价均为非负数,只要不折返代价就是最小的,可以将横向与纵向移动分开考虑,分别计算 startPos[0]homePos[0] 以及 startPos[1]homePos[1] 的代价。

由于起点与家的相对位置是不确定的,循环的步长(+1 还是 -1)、结束条件(大于等于 还是 小于等于)需要动态定义。

网友题解则是直接减掉了起点的代价,统一由小坐标到大坐标累加成本。

代码


/**
 * @date 2026-04-07 11:21
 */
public class MinCost2087 {

    public int minCost(int[] startPos, int[] homePos, int[] rowCosts, int[] colCosts) {
        int res = 0;
        int sx = startPos[0];
        int sy = startPos[1];
        int hx = homePos[0];
        int hy = homePos[1];
        int dx = sx <= hx ? 1 : -1;
        int dy = sy <= hy ? 1 : -1;
        for (int i = sx + dx; dx == 1 ? i <= hx : i >= hx; i += dx) {
            res += rowCosts[i];
        }
        for (int i = sy + dy; dy == 1 ? i <= hy : i >= hy; i += dy) {
            res += colCosts[i];
        }
        return res;
    }

}

性能