3392.统计符合条件长度为3的子数组数目

目标

给你一个整数数组 nums ,请你返回长度为 3 的 子数组,满足第一个数和第三个数的和恰好为第二个数的一半。

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

示例 1:

输入:nums = [1,2,1,4,1]
输出:1
解释:
只有子数组 [1,4,1] 包含 3 个元素且第一个和第三个数字之和是中间数字的一半。number.

示例 2:

输入:nums = [1,1,1]
输出:0
解释:
[1,1,1] 是唯一长度为 3 的子数组,但第一个数和第三个数的和不是第二个数的一半。

说明:

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

思路

求数组 nums 中长度为 3 且满足条件的子数组个数,子数组需满足两边元素和 是中间元素的一半。

依题意模拟即可。

代码


/**
 * @date 2025-04-27 0:14
 */
public class CountSubarrays3392 {

    public int countSubarrays(int[] nums) {
        int n = nums.length;
        int left = 0;
        int res = 0;
        for (int right = 2; right < n; right++) {
            if ((nums[left++] + nums[right]) * 2 == nums[right - 1]) {
                res++;
            }
        }
        return res;
    }

}

性能

1399.统计最大组的数目

目标

给你一个整数 n 。请你先求出从 1 到 n 的每个整数 10 进制表示下的数位和(每一位上的数字相加),然后把数位和相等的数字放到同一个组中。

请你统计每个组中的数字数目,并返回数字数目并列最多的组有多少个。

示例 1:

输入:n = 13
输出:4
解释:总共有 9 个组,将 1 到 13 按数位求和后这些组分别是:
[1,10],[2,11],[3,12],[4,13],[5],[6],[7],[8],[9]。总共有 4 个组拥有的数字并列最多。

示例 2:

输入:n = 2
输出:2
解释:总共有 2 个大小为 1 的组 [1],[2]。

示例 3:

输入:n = 15
输出:6

示例 4:

输入:n = 24
输出:5

说明:

  • 1 <= n <= 10^4

思路

计算 1 ~ n 十进制下的数位和,将数位和相同的分成到一组,求组中数字个数并列最多的组有多少个。

根据题意模拟即可,也可以使用数位DP。

代码


/**
 * @date 2025-04-23 0:12
 */
public class CountLargestGroup1399 {

    public int countLargestGroup(int n) {
        int length = String.valueOf(n).length();
        int[] cnt = new int[9 * length + 1];
        int maxDigitSumCnt = 0;
        int res = 0;
        for (int i = 1; i <= n; i++) {
            int num = i;
            int sum = 0;
            while (num > 0) {
                sum += num % 10;
                num /= 10;
            }
            if (maxDigitSumCnt < ++cnt[sum]) {
                maxDigitSumCnt = cnt[sum];
                res = 1;
            } else if (maxDigitSumCnt == cnt[sum]) {
                res++;
            }
        }
        return res;
    }

}

性能

2176.统计数组中相等且可以被整除的数对

目标

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k ,请你返回满足 0 <= i < j < n ,nums[i] == nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。

示例 1:

输入:nums = [3,1,2,2,2,1,3], k = 2
输出:4
解释:
总共有 4 对数符合所有要求:
- nums[0] == nums[6] 且 0 * 6 == 0 ,能被 2 整除。
- nums[2] == nums[3] 且 2 * 3 == 6 ,能被 2 整除。
- nums[2] == nums[4] 且 2 * 4 == 8 ,能被 2 整除。
- nums[3] == nums[4] 且 3 * 4 == 12 ,能被 2 整除。

示例 2:

输入:nums = [1,2,3,4], k = 1
输出:0
解释:由于数组中没有重复数值,所以没有数对 (i,j) 符合所有要求。

说明:

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

思路

找出数组中下标不同的数对,使数对元素值相同且下标的乘积能够被 k 整除,返回满足要求的数对个数。

根据题意模拟即可。

有网友提出了使用原数组原地保存后面一个相同元素的下标,组成一个元素值相同的链表,避免重复判断不相等的元素。

也有网友提出了 O(nlogn) 的解法,与当前元素 nums[j] 组成合法数对需要满足 i < j,并且 nums[i] == nums[j] && (i * j) % k == 0nums[i] 必须提供因子 k2 = k / gcd(k, j),问题转化为查找 j 前,包含因子 k2 且与当前元素值相同的元素个数。提前预处理每一个数的因子列表,遍历当前值的因子列表,将每一个因子与当前值组成 key 并计数。

代码

/**
 * @date 2025-04-17 0:09
 */
public class CountPairs2176 {

    public int countPairs(int[] nums, int k) {
        int n = nums.length;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (nums[i] == nums[j] && i * j % k == 0){
                    res++;
                }
            }
        }
        return res;
    }
}

性能

1534.统计好三元组

目标

给你一个整数数组 arr ,以及 a、b、c 三个整数。请你统计其中好三元组的数量。

如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。

  • 0 <= i < j < k < arr.length
  • |arr[i] - arr[j]| <= a
  • |arr[j] - arr[k]| <= b
  • |arr[i] - arr[k]| <= c

其中 |x| 表示 x 的绝对值。

返回 好三元组的数量 。

示例 1:

输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。

示例 2:

输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。

说明:

  • 3 <= arr.length <= 100
  • 0 <= arr[i] <= 1000
  • 0 <= a, b, c <= 1000

思路

返回数组中的好三元组的数量,所谓好三元组就是前两个元素值差的绝对值小于 a,后两个元素元素值差的绝对值小于 b,首尾元素差的绝对值小于 c。

数据量不大可以直接暴力解。

网友提出了另一种解法,枚举 j、k,确定 i 的范围,然后使用前缀和快速获取范围内的元素个数。

代码


/**
 * @date 2025-04-14 0:06
 */
public class CountGoodTriplets1534 {

    public int countGoodTriplets(int[] arr, int a, int b, int c) {
        int n = arr.length;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    if (Math.abs(arr[i] - arr[j]) <= a && Math.abs(arr[j] - arr[k]) <= b && Math.abs(arr[i] - arr[k]) <= c) {
                        res++;
                    }
                }
            }
        }
        return res;
    }
}

性能

2843.统计对称整数的数目

目标

给你两个正整数 low 和 high 。

对于一个由 2 * n 位数字组成的整数 x ,如果其前 n 位数字之和与后 n 位数字之和相等,则认为这个数字是一个对称整数。

返回在 [low, high] 范围内的 对称整数的数目 。

示例 1:

输入:low = 1, high = 100
输出:9
解释:在 1 到 100 范围内共有 9 个对称整数:11、22、33、44、55、66、77、88 和 99 。

示例 2:

输入:low = 1200, high = 1230
输出:4
解释:在 1200 到 1230 范围内共有 4 个对称整数:1203、1212、1221 和 1230 。

说明:

  • 1 <= low <= high <= 10^4

思路

计算给定区间内的对称整数数目,对称整数的长度为偶数,且左边数字之和等于右边数字之和。

数据范围小可以直接暴力枚举。

代码

class Solution {
    public int countSymmetricIntegers(int low, int high) {
        int res = 0;
        for (int i = low; i <= high; i++) {
            String num = String.valueOf(i);
            int r = num.length();
            if (r % 2 == 1) {
                continue;
            }
            r--;
            int l = 0;
            int diff = 0;
            while (l < r) {
                diff += num.charAt(l++) - num.charAt(r--);
            }
            res += diff != 0 ? 0 : 1;
        }
        return res;
    }
}

性能

3375.使数组的值全部为K的最少操作次数

目标

给你一个整数数组 nums 和一个整数 k 。

如果一个数组中所有 严格大于 h 的整数值都 相等 ,那么我们称整数 h 是 合法的 。

比方说,如果 nums = [10, 8, 10, 8] ,那么 h = 9 是一个 合法 整数,因为所有满足 nums[i] > 9 的数都等于 10 ,但是 5 不是 合法 整数。

你可以对 nums 执行以下操作:

  • 选择一个整数 h ,它对于 当前 nums 中的值是合法的。
  • 对于每个下标 i ,如果它满足 nums[i] > h ,那么将 nums[i] 变为 h 。

你的目标是将 nums 中的所有元素都变为 k ,请你返回 最少 操作次数。如果无法将所有元素都变 k ,那么返回 -1 。

示例 1:

输入:nums = [5,2,5,4,5], k = 2
输出:2
解释:
依次选择合法整数 4 和 2 ,将数组全部变为 2 。

示例 2:

输入:nums = [2,1,2], k = 2
输出:-1
解释:
没法将所有值变为 2 。

示例 3:

输入:nums = [9,7,5,3], k = 1
输出:4
解释:
依次选择合法整数 7 ,5 ,3 和 1 ,将数组全部变为 1 。

说明:

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

思路

每次操作可以将数组中所有大于 h 的数变为 h,求将数组中所有元素变为 k 所需的最小操作数,如果无法实现返回 -1

如果数组中存在小于 k 的元素,无论如何也无法实现。否则,每次操作可以将最大元素全部变为次最大元素,问题变为统计数组中严格大于 k 的元素个数。

代码


/**
 * @date 2025-04-09 8:48
 */
public class MinOperations3375 {

    public int minOperations(int[] nums, int k) {
        Set<Integer> set = new HashSet<>();
        boolean lt = false;
        for (int num : nums) {
            if (num > k) {
                set.add(num);
            } else if (num < k) {
                lt = true;
            }
        }
        return lt ? -1 : Math.max(set.size(), 0);
    }

}

性能

3396.使数组元素互不相同所需的最少操作次数

目标

给你一个整数数组 nums,你需要确保数组中的元素 互不相同 。为此,你可以执行以下操作任意次:

  • 从数组的开头移除 3 个元素。如果数组中元素少于 3 个,则移除所有剩余元素。

注意:空数组也视作为数组元素互不相同。返回使数组元素互不相同所需的 最少操作次数 。

示例 1:

输入: nums = [1,2,3,4,2,3,3,5,7]
输出: 2
解释:
第一次操作:移除前 3 个元素,数组变为 [4, 2, 3, 3, 5, 7]。
第二次操作:再次移除前 3 个元素,数组变为 [3, 5, 7],此时数组中的元素互不相同。
因此,答案是 2。

示例 2:

输入: nums = [4,5,6,4,4]
输出: 2
解释:
第一次操作:移除前 3 个元素,数组变为 [4, 4]。
第二次操作:移除所有剩余元素,数组变为空。
因此,答案是 2。

示例 3:

输入: nums = [6,7,8,9]
输出: 0
解释:
数组中的元素已经互不相同,因此不需要进行任何操作,答案是 0。

说明:

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

思路

每次操作可以删除数组前三个元素,求使数组元素互不相同所需要的最小操作次数。

最直接的想法是记录数组中每个元素的出现次数,同时记录重复元素的个数,然后模拟删除操作,如果将某个元素的出现次数减为 1,那么将重复元素个数减 1,直到重复元素个数为 0,返回操作次数。

网友题解使用逆向思维,倒序遍历数组,直到遇到第一个重复的元素,由于操作是从头开始的,因此一定要删除该重复元素。假如下标是 i,那么需要操作 ⌈(i + 1)/3⌉ = i/3 + 1 次。

对于整数 a >= 0, b > 0,有 ⌈a/b⌉ = ⌊(a + b - 1)/b⌋,将 a = qb + r 带入分析即可。或者直接写 ⌊a/b⌋ + a % b > 0 ? 1 : 0

正向考虑稍微有点复杂,需要找到重复数字前一个下标中的最大下标。

代码


/**
 * @date 2025-04-08 8:43
 */
public class MinimumOperations3396 {

    public int minimumOperations_v1(int[] nums) {
        int[] index = new int[101];
        Set<Integer> set = new HashSet<>();
        Arrays.fill(index, -1);
        int maxIndex = -1;
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            if (set.contains(num)) {
                maxIndex = Math.max(maxIndex, index[num]);
            }
            set.add(num);
            index[num] = i;
        }
        if (maxIndex == -1) {
            return 0;
        } else {
            return maxIndex / 3 + 1;
        }
    }

    public int minimumOperations(int[] nums) {
        int[] cnt = new int[101];
        Queue<Integer> q = new ArrayDeque<>();
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            q.offer(num);
            if (++cnt[num] > 1) {
                set.add(num);
            }
        }
        if (set.size() == 0) {
            return 0;
        }
        int sameCnt = set.size();
        int res = 0;
        while (!q.isEmpty()) {
            res++;
            for (int i = 0; !q.isEmpty() && i < 3; i++) {
                if (--cnt[q.poll()] == 1) {
                    sameCnt--;
                }
            }
            if (sameCnt == 0) {
                break;
            }
        }
        return res;
    }

}

性能

1863.找出所有子集的异或总和再求和

目标

一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 空 ,则异或总和为 0 。

  • 例如,数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1 。

给你一个数组 nums ,请你求出 nums 中每个 子集 的 异或总和 ,计算并返回这些值相加之 和 。

注意:在本题中,元素 相同 的不同子集应 多次 计数。

数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a 。

示例 1:

输入:nums = [1,3]
输出:6
解释:[1,3] 共有 4 个子集:
- 空子集的异或总和是 0 。
- [1] 的异或总和为 1 。
- [3] 的异或总和为 3 。
- [1,3] 的异或总和为 1 XOR 3 = 2 。
0 + 1 + 3 + 2 = 6

示例 2:

输入:nums = [5,1,6]
输出:28
解释:[5,1,6] 共有 8 个子集:
- 空子集的异或总和是 0 。
- [5] 的异或总和为 5 。
- [1] 的异或总和为 1 。
- [6] 的异或总和为 6 。
- [5,1] 的异或总和为 5 XOR 1 = 4 。
- [5,6] 的异或总和为 5 XOR 6 = 3 。
- [1,6] 的异或总和为 1 XOR 6 = 7 。
- [5,1,6] 的异或总和为 5 XOR 1 XOR 6 = 2 。
0 + 5 + 1 + 6 + 4 + 3 + 7 + 2 = 28

示例 3:

输入:nums = [3,4,5,6,7,8]
输出:480
解释:每个子集的全部异或总和值之和为 480 。

说明:

  • 1 <= nums.length <= 12
  • 1 <= nums[i] <= 20

思路

计算数组所有子序列的异或和之和。

dfs 枚举所有子序列。

代码


/**
 * @date 2025-04-05 19:47
 */
public class SubsetXORSum1863 {

    int res;

    public int subsetXORSum(int[] nums) {
        dfs(0, 0, nums);
        return res;
    }

    public void dfs(int index, int xor, int[] nums) {
        int n = nums.length;
        if (index == n) {
            res += xor;
            return;
        }
        dfs(index + 1, xor, nums);
        dfs(index + 1, xor ^ nums[index], nums);
    }

}

性能

2873.有序三元组中的最大值I

目标

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

请你从所有满足 i < j < k 的下标三元组 (i, j, k) 中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回 0 。

下标三元组 (i, j, k) 的值等于 (nums[i] - nums[j]) * nums[k] 。

示例 1:

输入:nums = [12,6,1,2,7]
输出:77
解释:下标三元组 (0, 2, 4) 的值是 (nums[0] - nums[2]) * nums[4] = 77 。
可以证明不存在值大于 77 的有序下标三元组。

示例 2:

输入:nums = [1,10,3,4,19]
输出:133
解释:下标三元组 (1, 2, 4) 的值是 (nums[1] - nums[2]) * nums[4] = 133 。
可以证明不存在值大于 133 的有序下标三元组。 

示例 3:

输入:nums = [1,2,3]
输出:0
解释:唯一的下标三元组 (0, 1, 2) 的值是一个负数,(nums[0] - nums[1]) * nums[2] = -3 。因此,答案是 0 。

说明:

  • 3 <= nums.length <= 100
  • 1 <= nums[i] <= 10^6

思路

求数组 nums 的下标 0 <= i < j < k < n(nums[i] - nums[j]) * nums[k] 的最大值。

数据范围不大可以暴力解。

由于数组元素为正数,nums[k] > 0,要使 (nums[i] - nums[j]) * nums[k] 最大,我们可以枚举 k,在区间 [0, k) 上找到 i < j 使 (nums[i] - nums[j]) 最大,如果是负数直接取 0

维护 [0, i] 的最大值,用最大值减去当前值得到最大差值,有了最大差值直接乘以 nums[i + 1],在遍历过程中取最大值即可。

代码


/**
 * @date 2025-04-02 0:54
 */
public class MaximumTripletValue2873 {

    public long maximumTripletValue_v1(int[] nums) {
        long res = 0L;
        int n = nums.length;
        int max = 0;
        long maxDiff = 0;
        for (int i = 0; i < n - 1; i++) {
            max = Math.max(max, nums[i]);
            maxDiff = Math.max(maxDiff, max - nums[i]);
            res = Math.max(res, maxDiff * nums[i + 1]);
        }
        return res;
    }

    public long maximumTripletValue(int[] nums) {
        long res = 0L;
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    res = Math.max((long)(nums[i] - nums[j]) * nums[k], res);
                }
            }
        }
        return res;
    }
}

性能

2278.字母在字符串中的百分比

目标

给你一个字符串 s 和一个字符 letter ,返回在 s 中等于 letter 字符所占的 百分比 ,向下取整到最接近的百分比。

示例 1:

输入:s = "foobar", letter = "o"
输出:33
解释:
等于字母 'o' 的字符在 s 中占到的百分比是 2 / 6 * 100% = 33% ,向下取整,所以返回 33 。

示例 2:

输入:s = "jjjj", letter = "k"
输出:0
解释:
等于字母 'k' 的字符在 s 中占到的百分比是 0% ,所以返回 0 。

说明:

  • 1 <= s.length <= 100
  • s 由小写英文字母组成
  • letter 是一个小写英文字母

思路

统计给定字符在字符串中出现的百分比,要求向下取整。即 100 * cnt / total

代码


/**
 * @date 2025-03-31 8:43
 */
public class PercentageLetter2278 {

    public int percentageLetter(String s, char letter) {
        int n = s.length();
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (s.charAt(i) == letter) {
                res++;
            }
        }
        return res * 100 / n;
    }
}

性能