2033.获取单值网格的最小操作数

目标

给你一个大小为 m x n 的二维整数网格 grid 和一个整数 x 。每一次操作,你可以对 grid 中的任一元素 加 x 或 减 x 。

单值网格 是全部元素都相等的网格。

返回使网格化为单值网格所需的 最小 操作数。如果不能,返回 -1 。

示例 1:

输入:grid = [[2,4],[6,8]], x = 2
输出:4
解释:可以执行下述操作使所有元素都等于 4 : 
- 2 加 x 一次。
- 6 减 x 一次。
- 8 减 x 两次。
共计 4 次操作。

示例 2:

输入:grid = [[1,5],[2,3]], x = 1
输出:5
解释:可以使所有元素都等于 3 。

示例 3:

输入:grid = [[1,2],[3,4]], x = 2
输出:-1
解释:无法使所有元素相等。

说明:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10^5
  • 1 <= m * n <= 10^5
  • 1 <= x, grid[i][j] <= 10^4

思路

有一个 m x n 矩阵,每次操作可以将矩阵中的任意元素 +x 或者 -x,求使得矩阵所有元素相等所需的最小操作,如果不能返回 -1

所有元素对 x 取模余数相同才有解,因为操作无法改变这个余数,而目标是将元素变成同一个数。

如果有解,可以将共同的余数减去,将每个元素都除以 x,然后令 x = 1,问题变成找到一个值,使得所有元素到这个值的距离之和最小。

这是一个 L1 优化问题,它的解是中位数,可以先排序,再取中间的元素即可。

代码


/**
 * @date 2026-04-28 9:35
 */
public class MinOperations2033 {

    public int minOperations(int[][] grid, int x) {
        int m = grid.length;
        int n = grid[0].length;
        int[] arr = new int[m * n];
        int rem = grid[0][0] % x;
        int k = 0;
        for (int[] row : grid) {
            for (int e : row) {
                if (e % x != rem) {
                    return -1;
                }
                arr[k++] = e / x;
            }
        }
        Arrays.sort(arr);
        int median = arr[arr.length / 2];
        int res = 0;
        for (int num : arr) {
            res += Math.abs(num - median);
        }
        return res;
    }
}

性能

3464.正方形上的点之间的最大距离

目标

给你一个整数 side,表示一个正方形的边长,正方形的四个角分别位于笛卡尔平面的 (0, 0) ,(0, side) ,(side, 0) 和 (side, side) 处。

同时给你一个 正整数 k 和一个二维整数数组 points,其中 points[i] = [xi, yi] 表示一个点在正方形边界上的坐标。

你需要从 points 中选择 k 个元素,使得任意两个点之间的 最小 曼哈顿距离 最大化 。

返回选定的 k 个点之间的 最小 曼哈顿距离的 最大 可能值。

两个点 (xi, yi) 和 (xj, yj) 之间的曼哈顿距离为 |xi - xj| + |yi - yj|。

示例 1:

输入: side = 2, points = [[0,2],[2,0],[2,2],[0,0]], k = 4
输出: 2
解释:
选择所有四个点。

示例 2:

输入: side = 2, points = [[0,0],[1,2],[2,0],[2,2],[2,1]], k = 4
输出: 1
解释:
选择点 (0, 0) ,(2, 0) ,(2, 2) 和 (2, 1)。

示例 3:

输入: side = 2, points = [[0,0],[0,1],[0,2],[1,2],[2,0],[2,2],[2,1]], k = 5
输出: 1
解释:
选择点 (0, 0) ,(0, 1) ,(0, 2) ,(1, 2) 和 (2, 2)。

说明:

  • 1 <= side <= 10^9
  • 4 <= points.length <= min(4 side, 15 10^3)
  • points[i] == [xi, yi]
  • 输入产生方式如下:
    • points[i] 位于正方形的边界上。
    • 所有 points[i] 都 互不相同 。
  • 4 <= k <= min(25, points.length)

思路

代码

性能

3783.整数的镜像距离

目标

给你一个整数 n。

定义它的 镜像距离 为:abs(n - reverse(n)),其中 reverse(n) 表示将 n 的数字反转后形成的整数。

返回表示 n 的镜像距离的整数。

其中,abs(x) 表示 x 的绝对值。

示例 1:

输入: n = 25
输出: 27
解释:
reverse(25) = 52。
因此,答案为 abs(25 - 52) = 27。

示例 2:

输入: n = 10
输出: 9
解释:
reverse(10) = 01,即 1。
因此,答案为 abs(10 - 1) = 9。

示例 3:

输入: n = 7
输出: 0
解释:
reverse(7) = 7。
因此,答案为 abs(7 - 7) = 0。

说明:

  • 1 <= n <= 10^9

思路

反转整数 n 的十进制表示,计算 abs(n - reverse(n))

代码


/**
 * @date 2026-04-18 0:30
 */
public class MirrorDistance3783 {

    public int mirrorDistance(int n) {
        int r = 0;
        int base = 10;
        int t = n;
        while (t > 0) {
            r = r * base + t % 10;
            t /= 10;
        }
        return Math.abs(r - n);
    }
}

性能

1415.长度为n的开心字符串中字典序第k小的字符串

目标

一个 「开心字符串」定义为:

  • 仅包含小写字母 ['a', 'b', 'c'].
  • 对所有在 1 到 s.length - 1 之间的 i ,满足 s[i] != s[i + 1] (字符串的下标从 1 开始)。

比方说,字符串 "abc","ac","b" 和 "abcbabcbcb" 都是开心字符串,但是 "aa","baa" 和 "ababbc" 都不是开心字符串。

给你两个整数 n 和 k ,你需要将长度为 n 的所有开心字符串按字典序排序。

请你返回排序后的第 k 个开心字符串,如果长度为 n 的开心字符串少于 k 个,那么请你返回 空字符串 。

示例 1:

输入:n = 1, k = 3
输出:"c"
解释:列表 ["a", "b", "c"] 包含了所有长度为 1 的开心字符串。按照字典序排序后第三个字符串为 "c" 。

示例 2:

输入:n = 1, k = 4
输出:""
解释:长度为 1 的开心字符串只有 3 个。

示例 3:

输入:n = 3, k = 9
输出:"cab"
解释:长度为 3 的开心字符串总共有 12 个 ["aba", "abc", "aca", "acb", "bab", "bac", "bca", "bcb", "cab", "cac", "cba", "cbc"] 。第 9 个字符串为 "cab"

示例 4:

输入:n = 2, k = 7
输出:""

示例 5:

输入:n = 10, k = 100
输出:"abacbabacb"

说明:

  • 1 <= n <= 10
  • 1 <= k <= 100

思路

使用小写字母 a b c 构造相邻不重复的长度为 n 的字符串,求字典序第 k 小的字符串。

按顺序回溯构造长度为 n 的字符串,直到第 k 个结束返回。

代码


/**
 * @date 2026-03-16 13:55
 */
public class GetHappyString1415 {

    private char[] curChar = new char[]{'a', 'b', 'c'};

    public String getHappyString(int n, int k) {
        List<String> list = new ArrayList<>(k);
        char[] chars = new char[n];
        dfs(n, k, '-', 0, chars, list);
        return list.size() < k ? "" : list.get(k - 1);
    }

    public void dfs(int n, int k, char prev, int l, char[] chars, List<String> list) {
        if (list.size() == k) {
            return;
        }
        if (l == n) {
            list.add(new String(chars));
            return;
        }
        for (char c : curChar) {
            if (c == prev) {
                continue;
            }
            chars[l] = c;
            dfs(n, k, c, l + 1, chars, list);
        }
    }

}

性能

3296.移山所需的最少秒数

目标

给你一个整数 mountainHeight 表示山的高度。

同时给你一个整数数组 workerTimes,表示工人们的工作时间(单位:秒)。

工人们需要 同时 进行工作以 降低 山的高度。对于工人 i :

  • 山的高度降低 x,需要花费 workerTimes[i] + workerTimes[i] 2 + ... + workerTimes[i] x 秒。例如:
    • 山的高度降低 1,需要 workerTimes[i] 秒。
    • 山的高度降低 2,需要 workerTimes[i] + workerTimes[i] * 2 秒,依此类推。

返回一个整数,表示工人们使山的高度降低到 0 所需的 最少 秒数。

示例 1:

输入: mountainHeight = 4, workerTimes = [2,1,1]
输出: 3
解释:
将山的高度降低到 0 的一种方式是:
工人 0 将高度降低 1,花费 workerTimes[0] = 2 秒。
工人 1 将高度降低 2,花费 workerTimes[1] + workerTimes[1] * 2 = 3 秒。
工人 2 将高度降低 1,花费 workerTimes[2] = 1 秒。
因为工人同时工作,所需的最少时间为 max(2, 3, 1) = 3 秒。

示例 2:

输入: mountainHeight = 10, workerTimes = [3,2,2,4]
输出: 12
解释:
工人 0 将高度降低 2,花费 workerTimes[0] + workerTimes[0] * 2 = 9 秒。
工人 1 将高度降低 3,花费 workerTimes[1] + workerTimes[1] * 2 + workerTimes[1] * 3 = 12 秒。
工人 2 将高度降低 3,花费 workerTimes[2] + workerTimes[2] * 2 + workerTimes[2] * 3 = 12 秒。
工人 3 将高度降低 2,花费 workerTimes[3] + workerTimes[3] * 2 = 12 秒。
所需的最少时间为 max(9, 12, 12, 12) = 12 秒。

示例 3:

输入: mountainHeight = 5, workerTimes = [1]
输出: 15
解释:
这个示例中只有一个工人,所以答案是 workerTimes[0] + workerTimes[0] * 2 + workerTimes[0] * 3 + workerTimes[0] * 4 + workerTimes[0] * 5 = 15 秒。

说明:

  • 1 <= mountainHeight <= 10^5
  • 1 <= workerTimes.length <= 10^4
  • 1 <= workerTimes[i] <= 10^6

思路

整数 mountainHeight 表示山的高度,workerTimes[i] 表示工人 i 的时效,工人 i 将山的高度降低 x 所需的时间是 workerTimes[i] + workerTimes[i] * 2 + ... + workerTimes[i] * x,求工人同时工作,将山的高度降为 0 所需要的最少时间。

移山所需的时间是所有工人消耗时间的最大值,最小化这个最大值,可以考虑二分答案。

工人 i 将高度降低 x 所需时间为 workerTimes[i] * (1 + 2 + …… + x) = workerTimes[i] * (1 + x) * x / 2。如果最大时间是 target,解方程可得工人 i 最多将高度降低 (sqrt(1 + (8 * target) / workerTimes[i]) - 1) / 2,只需判断降低的高度是否超过 mountainHeight 即可。

代码


/**
 * @date 2026-03-13 9:42
 */
public class MinNumberOfSeconds3296 {

    public long minNumberOfSeconds(int mountainHeight, int[] workerTimes) {
        int max = 0;
        int n = workerTimes.length;
        for (int wt : workerTimes) {
            max = Math.max(max, wt);
        }
        long p = (mountainHeight - 1) / n + 1;
        long l = 0L, r = max * (1 + p) * p / 2;
        long m = l + (r - l) / 2;
        while (l <= r) {
            if (check(mountainHeight, workerTimes, m)) {
                r = m - 1;
            } else {
                l = m + 1;
            }
            m = l + (r - l) / 2;
        }
        return l;
    }

    public boolean check(int mountainHeight, int[] workerTimes, long target) {
        int total = 0;
        for (int w : workerTimes) {
            total += (int) (Math.sqrt(1 + (8 * target) / w) - 1) / 2;
            if (total >= mountainHeight) {
                return true;
            }
        }
        return false;
    }

}

性能

1980.找出不同的二进制字符串

目标

给你一个字符串数组 nums ,该数组由 n 个 互不相同 的二进制字符串组成,且每个字符串长度都是 n 。请你找出并返回一个长度为 n 且 没有出现 在 nums 中的二进制字符串。如果存在多种答案,只需返回 任意一个 即可。

示例 1:

输入:nums = ["01","10"]
输出:"11"
解释:"11" 没有出现在 nums 中。"00" 也是正确答案。

示例 2:

输入:nums = ["00","01"]
输出:"11"
解释:"11" 没有出现在 nums 中。"10" 也是正确答案。

示例 3:

输入:nums = ["111","011","001"]
输出:"101"
解释:"101" 没有出现在 nums 中。"000"、"010"、"100"、"110" 也是正确答案。

说明:

  • n == nums.length
  • 1 <= n <= 16
  • nums[i].length == n
  • nums[i] 为 '0' 或 '1'
  • nums 中的所有字符串 互不相同

思路

n 个长度为 n 的二进制字符串数组,构造一个长度为 n 的字符串,使它与数组中的字符串不同,答案可能有多个返回任一一个即可。

康托对角线,构造字符串的第 i 个位置 与 nums[i] 的第 i 个位置不同,这样可以保证构造出的字符串与数组中的所有字符串都不同。

代码


/**
 * @date 2026-03-09 18:08
 */
public class FindDifferentBinaryString1980 {

    public String findDifferentBinaryString_v1(String[] nums) {
        int n = nums.length;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            int c = nums[i].charAt(i) - '0';
            sb.append(c ^ 1);
        }
        return sb.toString();
    }

}

性能

1680.连接连续二进制数字

目标

给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 10^9 + 7 取余的结果。

示例 1:

输入:n = 1
输出:1
解释:二进制的 "1" 对应着十进制的 1 。

示例 2:

输入:n = 3
输出:27
解释:二进制下,1,2 和 3 分别对应 "1" ,"10" 和 "11" 。
将它们依次连接,我们得到 "11011" ,对应着十进制的 27 。

示例 3:

输入:n = 12
输出:505379714
解释:连接结果为 "1101110010111011110001001101010111100" 。
对应的十进制数字为 118505380540 。
对 10^9 + 7 取余后,结果为 505379714 。

说明:

  • 1 <= n <= 10^5

思路

拼接 1 ~ n 的二进制表示,将结果对应的十进制数对 10^9 + 7 取模。

遍历 1 ~ n 模拟拼接过程,将之前拼接的数字左移当前数字的 bitLength 并对 mod 取模。拼接 a b c 可以视为 (a * 2^bitLength(b) + b) * 2^bitLength(c) + c,可以先对括号内的运算取模,即 (a << bitLength(b)) + b ) % mod

代码


/**
 * @date 2026-02-28 9:18
 */
public class ConcatenatedBinary1680 {

    public int concatenatedBinary(int n) {
        int mod = 1000000007;
        long res = 0L;
        for (int i = 1; i <= n; i++) {
            int bl = 32 - Integer.numberOfLeadingZeros(i);
            res = ((res << bl) + i) % mod;
        }
        return (int) res;
    }

}

性能

3666.使二进制字符串全为1的最少操作次数

目标

给你一个二进制字符串 s 和一个整数 k。

在一次操作中,你必须选择 恰好 k 个 不同的 下标,并将每个 '0' 翻转 为 '1',每个 '1' 翻转为 '0'。

返回使字符串中所有字符都等于 '1' 所需的 最少 操作次数。如果不可能,则返回 -1。

示例 1:

输入: s = "110", k = 1
输出: 1
解释:
s 中有一个 '0'。
由于 k = 1,我们可以直接在一次操作中翻转它。

示例 2:

输入: s = "0101", k = 3
输出: 2
解释:
每次操作选择 k = 3 个下标的一种最优操作方案是:
操作 1:翻转下标 [0, 1, 3]。s 从 "0101" 变为 "1000"。
操作 2:翻转下标 [1, 2, 3]。s 从 "1000" 变为 "1111"。
因此,最少操作次数为 2。

示例 3:

输入: s = "101", k = 2
输出: -1
解释:
由于 k = 2 且 s 中只有一个 '0',因此不可能通过翻转恰好 k 个位来使所有字符变为 '1'。因此,答案是 -1。

说明:

  • 1 <= s.length <= 10^5
  • s[i] 的值为 '0' 或 '1'。
  • 1 <= k <= s.length

思路

代码

性能

1404.将二进制表示减到1的步骤数

目标

给你一个以二进制形式表示的数字 s 。请你返回按下述规则将其减少到 1 所需要的步骤数:

如果当前数字为偶数,则将其除以 2 。

如果当前数字为奇数,则将其加上 1 。

题目保证你总是可以按上述规则将测试用例变为 1 。

示例 1:

输入:s = "1101"
输出:6
解释:"1101" 表示十进制数 13 。
Step 1) 13 是奇数,加 1 得到 14 
Step 2) 14 是偶数,除 2 得到 7
Step 3) 7  是奇数,加 1 得到 8
Step 4) 8  是偶数,除 2 得到 4  
Step 5) 4  是偶数,除 2 得到 2 
Step 6) 2  是偶数,除 2 得到 1  

示例 2:

输入:s = "10"
输出:1
解释:"10" 表示十进制数 2 。
Step 1) 2 是偶数,除 2 得到 1 

示例 3:

输入:s = "1"
输出:0

说明:

  • 1 <= s.length <= 500
  • s 由字符 '0' 或 '1' 组成。
  • s[0] == '1'

思路

有一个二进制表示的数字,如果当前是偶数,可以将数字除以 2,即右移;如果当前是奇数,将数字加 1。求将数字变为 1 需要的操作数。

根据题意模拟,使用变量 carry 保存进位,从右向左遍历,判断最后一位数字 dcarry 相加后的奇偶性,即 sum = d + carry。如果 sum 是偶数(0 或者 2),右移后这一位消失,只需考虑 carry = sum / 2;如果 sum 是奇数,加一后进位 carry = 1,此时当前数字变为偶数,可以将这两个操作合并。

代码


/**
 * @date 2026-02-26 8:51
 */
public class NumSteps1404 {

    public int numSteps(String s) {
        int res = 0;
        int n = s.length();
        char[] chars = s.toCharArray();
        int carry = 0;
        for (int i = n - 1; i > 0; i--) {
            int d = chars[i] - '0';
            int sum = d + carry;
            if (sum % 2 == 0) {
                carry = sum / 2;
                res++;
            } else {
                carry = 1;
                res += 2;
            }
        }
        return res + carry;
    }

}

性能

762.二进制表示中质数个计算置位

目标

给你两个整数 left 和 right ,在闭区间 [left, right] 范围内,统计并返回 计算置位位数为质数 的整数个数。

计算置位位数 就是二进制表示中 1 的个数。

例如, 21 的二进制表示 10101 有 3 个计算置位。

示例 1:

输入:left = 6, right = 10
输出:4
解释:
6 -> 110 (2 个计算置位,2 是质数)
7 -> 111 (3 个计算置位,3 是质数)
9 -> 1001 (2 个计算置位,2 是质数)
10-> 1010 (2 个计算置位,2 是质数)
共计 4 个计算置位为质数的数字。

示例 2:

输入:left = 10, right = 15
输出:5
解释:
10 -> 1010 (2 个计算置位, 2 是质数)
11 -> 1011 (3 个计算置位, 3 是质数)
12 -> 1100 (2 个计算置位, 2 是质数)
13 -> 1101 (3 个计算置位, 3 是质数)
14 -> 1110 (3 个计算置位, 3 是质数)
15 -> 1111 (4 个计算置位, 4 不是质数)
共计 5 个计算置位为质数的数字。

说明:

  • 1 <= left <= right <= 10^6
  • 0 <= right - left <= 10^4

思路

返回 [left, right] 范围内的数字中,二进制表示中 1 的个数为质数的数字个数。

1 ~ 10^6 的数位长度不超过 201 ~ 20 之间的质数有 2, 3, 5, 7, 11, 13, 17, 19。枚举每个数字计算其二进制表示中 1 的个数,判断是否是质数。

代码


/**
 * @date 2026-02-24 15:46
 */
public class CountPrimeSetBits762 {

    public static Set<Integer> primes;

    static {
        primes = new HashSet<>();
        primes.add(2);
        primes.add(3);
        primes.add(5);
        primes.add(7);
        primes.add(11);
        primes.add(13);
        primes.add(17);
        primes.add(19);
    }

    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for (int i = left; i <= right; i++) {
            int bitCount = Integer.bitCount(i);
            if (primes.contains(bitCount)) {
                res++;
            }
        }
        return res;
    }

}

性能