2900.最长相邻不相等子序列I

目标

给你一个下标从 0 开始的字符串数组 words ,和一个下标从 0 开始的 二进制 数组 groups ,两个数组长度都是 n 。

你需要从 words 中选出 最长子序列。如果对于序列中的任何两个连续串,二进制数组 groups 中它们的对应元素不同,则 words 的子序列是不同的。

正式来说,你需要从下标 [0, 1, ..., n - 1] 中选出一个 最长子序列 ,将这个子序列记作长度为 k 的 [i0, i1, ..., ik - 1] ,对于所有满足 0 <= j < k - 1 的 j 都有 groups[ij] != groups[ij + 1] 。

请你返回一个字符串数组,它是下标子序列 依次 对应 words 数组中的字符串连接形成的字符串数组。如果有多个答案,返回 任意 一个。

注意:words 中的元素是不同的 。

示例 1:

输入:words = ["e","a","b"], groups = [0,0,1]
输出:["e","b"]
解释:一个可行的子序列是 [0,2] ,因为 groups[0] != groups[2] 。
所以一个可行的答案是 [words[0],words[2]] = ["e","b"] 。
另一个可行的子序列是 [1,2] ,因为 groups[1] != groups[2] 。
得到答案为 [words[1],words[2]] = ["a","b"] 。
这也是一个可行的答案。
符合题意的最长子序列的长度为 2 。

示例 2:

输入:words = ["a","b","c","d"], groups = [1,0,1,1]
输出:["a","b","c"]
解释:一个可行的子序列为 [0,1,2] 因为 groups[0] != groups[1] 且 groups[1] != groups[2] 。
所以一个可行的答案是 [words[0],words[1],words[2]] = ["a","b","c"] 。
另一个可行的子序列为 [0,1,3] 因为 groups[0] != groups[1] 且 groups[1] != groups[3] 。
得到答案为 [words[0],words[1],words[3]] = ["a","b","d"] 。
这也是一个可行的答案。
符合题意的最长子序列的长度为 3 。

说明:

  • 1 <= n == words.length == groups.length <= 100
  • 1 <= words[i].length <= 10
  • groups[i] 是 0 或 1。
  • words 中的字符串 互不相同 。
  • words[i] 只包含小写英文字母。

思路

有一个长度为 n 的字符串数组 words,其中的字符串互不相同,同时它对应一个相同长度的二进制数组 groups,从二进制数组中找出一个相邻元素不同的最长子序列,并返回其在 words 中对应的子序列。

如何找相邻元素不同的最长子序列?只需选择所有数组元素,然后将相邻元素相同的元素删掉即可。

代码


/**
 * @date 2025-05-15 8:51
 */
public class GetLongestSubsequence2900 {

    public List<String> getLongestSubsequence(String[] words, int[] groups) {
        List<String> res = new ArrayList<>();
        int prev = 0;
        int n = words.length;
        res.add(words[0]);
        for (int i = 1; i < n; i++) {
            if (groups[prev] != groups[i]){
                res.add(words[i]);
                prev = i;
            }
        }
        return res;
    }
}

性能

2094.找出3位偶数

目标

给你一个整数数组 digits ,其中每个元素是一个数字(0 - 9)。数组中可能存在重复元素。

你需要找出 所有 满足下述条件且 互不相同 的整数:

  • 该整数由 digits 中的三个元素按 任意 顺序 依次连接 组成。
  • 该整数不含 前导零
  • 该整数是一个 偶数

例如,给定的 digits 是 [1, 2, 3] ,整数 132 和 312 满足上面列出的全部条件。

将找出的所有互不相同的整数按 递增顺序 排列,并以数组形式返回。

示例 1:

输入:digits = [2,1,3,0]
输出:[102,120,130,132,210,230,302,310,312,320]
解释:
所有满足题目条件的整数都在输出数组中列出。 
注意,答案数组中不含有 奇数 或带 前导零 的整数。

示例 2:

输入:digits = [2,2,8,8,2]
输出:[222,228,282,288,822,828,882]
解释:
同样的数字(0 - 9)在构造整数时可以重复多次,重复次数最多与其在 digits 中出现的次数一样。 
在这个例子中,数字 8 在构造 288、828 和 882 时都重复了两次。

示例 3:

输入:digits = [3,7,5]
输出:[]
解释:
使用给定的 digits 无法构造偶数。

说明:

  • 3 <= digits.length <= 100
  • 0 <= digits[i] <= 9

思路

有一个元素值为 0 ~ 9 的数组,返回由数组中的数字组成的不含前导 0 且长度为 3 的偶数。

暴力枚举每个数位上可能的选择,判断是否为偶数。

代码


/**
 * @date 2025-05-12 8:50
 */
public class FindEvenNumbers2094 {

    public int[] findEvenNumbers(int[] digits) {
        int n = digits.length;
        Set<Integer> set = new HashSet<>();
        boolean[] visited = new boolean[n];
        for (int i = 0; i < n; i++) {
            if (digits[i] == 0) {
                continue;
            }
            visited[i] = true;
            for (int j = 0; j < n; j++) {
                if (visited[j]) {
                    continue;
                }
                visited[j] = true;
                for (int k = 0; k < n; k++) {
                    if (visited[k]) {
                        continue;
                    }
                    int num = digits[i] * 100 + digits[j] * 10 + digits[k];
                    if (num % 2 == 0) {
                        set.add(num);
                    }
                }
                visited[j] = false;
            }
            visited[i] = false;
        }
        int[] res = new int[set.size()];
        ArrayList<Integer> list = new ArrayList<>(set);
        for (int i = 0; i < list.size(); i++) {
            res[i] = list.get(i);
        }
        Arrays.sort(res);
        return res;
    }

}

性能

1550.存在连续三个奇数的数组

目标

给你一个整数数组 arr,请你判断数组中是否存在连续三个元素都是奇数的情况:如果存在,请返回 true ;否则,返回 false 。

示例 1:

输入:arr = [2,6,4,1]
输出:false
解释:不存在连续三个元素都是奇数的情况。

示例 2:

输入:arr = [1,2,34,3,4,5,7,23,12]
输出:true
解释:存在连续三个元素都是奇数的情况,即 [5,7,23] 。

说明:

  • 1 <= arr.length <= 1000
  • 1 <= arr[i] <= 1000

思路

判断数组中是否存在三个连续的奇数。

使用定长滑动窗口。

代码


/**
 * @date 2025-05-11 0:15
 */
public class ThreeConsecutiveOdds1550 {

    public boolean threeConsecutiveOdds(int[] arr) {
        int n = arr.length;
        int left = 0;
        for (int right = 0; right < n; right++) {
            if (arr[right] % 2 == 0) {
                left = right + 1;
                continue;
            }
            if (right - left == 2) {
                return true;
            }
        }
        return false;
    }

}

性能

1920.基于排列构建数组

目标

给你一个 从 0 开始的排列 nums(下标也从 0 开始)。请你构建一个 同样长度 的数组 ans ,其中,对于每个 i(0 <= i < nums.length),都满足 ans[i] = nums[nums[i]] 。返回构建好的数组 ans 。

从 0 开始的排列 nums 是一个由 0 到 nums.length - 1(0 和 nums.length - 1 也包含在内)的不同整数组成的数组。

示例 1:

输入:nums = [0,2,1,5,3,4]
输出:[0,1,2,4,5,3]
解释:数组 ans 构建如下:
ans = [nums[nums[0]], nums[nums[1]], nums[nums[2]], nums[nums[3]], nums[nums[4]], nums[nums[5]]]
    = [nums[0], nums[2], nums[1], nums[5], nums[3], nums[4]]
    = [0,1,2,4,5,3]

示例 2:

输入:nums = [5,0,1,2,3,4]
输出:[4,5,0,1,2,3]
解释:数组 ans 构建如下:
ans = [nums[nums[0]], nums[nums[1]], nums[nums[2]], nums[nums[3]], nums[nums[4]], nums[nums[5]]]
    = [nums[5], nums[0], nums[1], nums[2], nums[3], nums[4]]
    = [4,5,0,1,2,3]

说明:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] < nums.length
  • nums 中的元素 互不相同

进阶:你能在不使用额外空间的情况下解决此问题吗(即 O(1) 内存)?

思路

数组 nums 是一个 0 ~ n - 1 的排列,构建一个新数组 res 满足 res[i] = nums[nums[i]]

一般的解法是创建一个新数组,依题意设置对应的值。

进阶解法需要满足空间复杂度为 O(1),即原地修改。可以将结果左移 10 位保存到原数组,最后将元素值右移 10 位还原结果。

代码


/**
 * @date 2025-05-06 8:43
 */
public class BuildArray1920 {

    public int[] buildArray(int[] nums) {
        int mask = 1023;
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] | ((nums[nums[i]] & mask) << 10);
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] >>= 10;
        }
        return nums;
    }

}

性能

1128.等价多米诺骨牌对的数量

目标

给你一组多米诺骨牌 dominoes 。

形式上,dominoes[i] = [a, b] 与 dominoes[j] = [c, d] 等价 当且仅当 (a == c 且 b == d) 或者 (a == d 且 b == c) 。即一张骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌。

在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。

示例 1:

输入:dominoes = [[1,2],[2,1],[3,4],[5,6]]
输出:1

示例 2:

输入:dominoes = [[1,2],[1,2],[1,1],[1,2],[2,2]]
输出:3

说明:

  • 1 <= dominoes.length <= 4 * 10^4
  • dominoes[i].length == 2
  • 1 <= dominoes[i][j] <= 9

思路

计算二维数组中相同元素组成的下标对 (i, j) 个数,这里相同元素指 (dominoes[i][0] == dominoes[j][0] && dominoes[i][1] == dominoes[j][1]) || (dominoes[i][0] == dominoes[j][1] && dominoes[i][1] == dominoes[j][0])

使用哈希表记录相同元素的出现次数,key 可以使用 dominoes[i][0] << 4 | dominoes[i][1]dominoes[i][1] << 4 | dominoes[i][0] 表示。

代码


/**
 * @date 2025-05-04 17:38
 */
public class NumEquivDominoPairs1128 {

    public int numEquivDominoPairs(int[][] dominoes) {
        Map<Integer, Integer> map = new HashMap<>();
        int res = 0;
        for (int[] dominoe : dominoes) {
            int key = dominoe[0] << 4 | dominoe[1];
            res += map.getOrDefault(key, 0);
            map.merge(key, 1, Integer::sum);
            if (dominoe[0] != dominoe[1]) {
                map.merge(dominoe[1] << 4 | dominoe[0], 1, Integer::sum);
            }
        }
        return res;
    }

}

性能

1295.统计位数为偶数的数字

目标

给你一个整数数组 nums,请你返回其中包含 偶数 个数位的数字的个数。

示例 1:

输入:nums = [12,345,2,6,7896]
输出:2
解释:
12 是 2 位数字(位数为偶数) 
345 是 3 位数字(位数为奇数)  
2 是 1 位数字(位数为奇数) 
6 是 1 位数字 位数为奇数) 
7896 是 4 位数字(位数为偶数)  
因此只有 12 和 7896 是位数为偶数的数字

示例 2:

输入:nums = [555,901,482,1771]
输出:1 
解释: 
只有 1771 是位数为偶数的数字。

说明:

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

思路

有一个正整数数组,返回数组中数位长度为偶数的元素个数。

代码


/**
 * @date 2025-04-30 8:42
 */
public class FindNumbers1295 {

    public int findNumbers(int[] nums) {
        int res = nums.length;
        for (int num : nums) {
            int cnt = 0;
            while (num > 0) {
                cnt++;
                num /= 10;
            }
            res -= cnt % 2;
        }
        return res;
    }

}

性能

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

性能