2680.最大或值

目标

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k 。每一次操作中,你可以选择一个数并将它乘 2 。

你最多可以进行 k 次操作,请你返回 nums[0] | nums[1] | ... | nums[n - 1] 的最大值。

a | b 表示两个整数 a 和 b 的 按位或 运算。

示例 1:

输入:nums = [12,9], k = 1
输出:30
解释:如果我们对下标为 1 的元素进行操作,新的数组为 [12,18] 。此时得到最优答案为 12 和 18 的按位或运算的结果,也就是 30 。

示例 2:

输入:nums = [8,1,2], k = 2
输出:35
解释:如果我们对下标 0 处的元素进行操作,得到新数组 [32,1,2] 。此时得到最优答案为 32|1|2 = 35 。

说明:

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

思路

有一个整数数组 nums,最多可以对它执行 k 次操作,每次操作可以任选一个数将其左移 1 位。求操作后数组所有元素的最大或值。

操作集中到同一个数上可以将最高有效位移到最高,少一次操作最高位就低一位。问题的关键在于,如果所有数字都有相同的最高有效位,移哪个是不确定的。

这时就需要枚举操作数了,每选择一个操作数就需要重新计算其余元素的或值。由于或运算没有逆运算,我们无法撤销已经或进去的值。

直接的想法是计算或前缀、后缀,枚举所有元素,将其左移 k 次,然后与前后缀进行或运算,取最大值。

网友提供了另一种基于位运算的解法,计算全体元素的或值,同时计算所有 bit 位上存在重复 1 的位置,通过异或运算将当前元素对或值的贡献抵消,然后补上重复位置上的 1

代码


/**
 * @date 2025-03-21 0:08
 */
public class MaximumOr2680 {

    public long maximumOr_v1(int[] nums, int k) {
        int n = nums.length;
        int or = 0;
        int multiBits = 0;
        for (int i = 0; i < n; i++) {
            multiBits = multiBits | or & nums[i];
            or = or | nums[i];
        }
        long res = 0L;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, or ^ nums[i] | multiBits | ((long) nums[i] << k));
        }
        return res;
    }

    public long maximumOr(int[] nums, int k) {
        int n = nums.length;
        int[] prefix = new int[n + 1];
        int[] suffix = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            prefix[i] = prefix[i - 1] | nums[i - 1];
            suffix[n - i] = suffix[n - i + 1] | nums[n - i];
        }
        long res = 0L;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, prefix[i] | suffix[i + 1] | ((long) nums[i] << k));
        }
        return res;
    }

}

性能

2610.转换二维数组

目标

给你一个整数数组 nums 。请你创建一个满足以下条件的二维数组:

  • 二维数组应该 只 包含数组 nums 中的元素。
  • 二维数组中的每一行都包含 不同 的整数。
  • 二维数组的行数应尽可能 少 。

返回结果数组。如果存在多种答案,则返回其中任何一种。

请注意,二维数组的每一行上可以存在不同数量的元素。

示例 1:

输入:nums = [1,3,4,1,2,3,1]
输出:[[1,3,4,2],[1,3],[1]]
解释:根据题目要求可以创建包含以下几行元素的二维数组:
- 1,3,4,2
- 1,3
- 1
nums 中的所有元素都有用到,并且每一行都由不同的整数组成,所以这是一个符合题目要求的答案。
可以证明无法创建少于三行且符合题目要求的二维数组。

示例 2:

输入:nums = [1,2,3,4]
输出:[[4,3,2,1]]
解释:nums 中的所有元素都不同,所以我们可以将其全部保存在二维数组中的第一行。

说明:

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

思路

将数组转化为二维数组,要求每一行元素没有重复,并且行数尽可能地少。

使用哈希表对相同元素计数,将所有 key 放入一行并将所有 key 的计数减一,如果计数减为 0 则删除 key,直到哈希表为空,时间复杂度为 O(n)

也可以将哈希表改为数组相当于计数排序。

代码


/**
 * @date 2025-03-19 0:21
 */
public class FindMatrix2610 {

    public List<List<Integer>> findMatrix(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.merge(num, 1, Integer::sum);
        }
        List<List<Integer>> res = new ArrayList<>();
        while (!map.isEmpty()) {
            res.add(new ArrayList<>());
            List<Integer> list = res.get(res.size() - 1);
            Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Integer, Integer> entry = iterator.next();
                list.add(entry.getKey());
                entry.setValue(entry.getValue() - 1);
                if (entry.getValue() == 0) {
                    iterator.remove();
                }
            }
        }
        return res;
    }

}

性能

1963.使字符串平衡的最小交换次数

目标

给你一个字符串 s ,下标从 0 开始 ,且长度为偶数 n 。字符串 恰好 由 n / 2 个开括号 '[' 和 n / 2 个闭括号 ']' 组成。

只有能满足下述所有条件的字符串才能称为 平衡字符串 :

  • 字符串是一个空字符串,或者
  • 字符串可以记作 AB ,其中 A 和 B 都是 平衡字符串 ,或者
  • 字符串可以写成 [C] ,其中 C 是一个 平衡字符串 。

你可以交换 任意 两个下标所对应的括号 任意 次数。

返回使 s 变成 平衡字符串 所需要的 最小 交换次数。

示例 1:

输入:s = "][]["
输出:1
解释:交换下标 0 和下标 3 对应的括号,可以使字符串变成平衡字符串。
最终字符串变成 "[[]]" 。

示例 2:

输入:s = "]]][[["
输出:2
解释:执行下述操作可以使字符串变成平衡字符串:
- 交换下标 0 和下标 4 对应的括号,s = "[]][][" 。
- 交换下标 1 和下标 5 对应的括号,s = "[[][]]" 。
最终字符串变成 "[[][]]" 。

示例 3:

输入:s = "[]"
输出:0
解释:这个字符串已经是平衡字符串。

说明:

  • n == s.length
  • 2 <= n <= 10^6
  • n 为偶数
  • s[i] 为'[' 或 ']'
  • 开括号 '[' 的数目为 n / 2 ,闭括号 ']' 的数目也是 n / 2

思路

有一个长度为偶数 n 的字符串,有 n / 2[]。定义括号能够匹配的字符串是平衡字符串。每一次操作可以交换任意两个下标对应的括号,求将字符串变平衡的最少操作次数。

[[]][][] 都是平衡字符串,如何确定该与哪个括号交换才能使操作次数最小?对于 ]][[][][,第一个与最后一个交换之后变为平衡字符串 [][][[]]。而如果选择与倒数第二个 [ 交换,会变成 []][,需要再交换一次才能变为平衡字符串。

平衡字符串有这样的性质:其前缀中 [ 的个数总是大于等于 ] 的个数。如果发现 ] 的个数超过 [,那么必须要进行交换。最好将它交换到最后,因为这样可以减少它在前面前缀中对 ] 的贡献,将交换操作尽可能地延后,减小必须进行交换的次数。

也可以先将平衡的括号消掉,剩下的必然是 ]]]......[[[ 的形式。交换一次最多可以抵消 4 个,最少抵消 2 个,即 (stack.size() + 2) / 4

代码


/**
 * @date 2025-03-17 16:32
 */
public class MinSwaps1963 {

    public int minSwaps_v1(String s) {
        int n = s.length();
        int left = 0, right = 0;
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c == '[') {
                left++;
            } else if (c == ']' && left > 0) {
                left--;
            } else {
                right++;
            }
        }
        return (left + right + 2) / 4;
    }

    public int minSwaps(String s) {
        int n = s.length();
        ArrayDeque<Character> stack = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c == ']' && !stack.isEmpty() && stack.peek() == '[') {
                stack.pop();
            } else {
                stack.push(c);
            }
        }
        return (stack.size() + 2) / 4;
    }

}

性能

3306.元音辅音字符串计数II

目标

给你一个字符串 word 和一个 非负 整数 k。

返回 word 的 子字符串 中,每个元音字母('a'、'e'、'i'、'o'、'u')至少 出现一次,并且 恰好 包含 k 个辅音字母的子字符串的总数。

示例 1:

输入:word = "aeioqq", k = 1
输出:0
解释:
不存在包含所有元音字母的子字符串。

示例 2:

输入:word = "aeiou", k = 0
输出:1
解释:
唯一一个包含所有元音字母且不含辅音字母的子字符串是 word[0..4],即 "aeiou"。

示例 3:

输入:word = "ieaouqqieaouqq", k = 1
输出:3
解释:
包含所有元音字母并且恰好含有一个辅音字母的子字符串有:
word[0..5],即 "ieaouq"。
word[6..11],即 "qieaou"。
word[7..12],即 "ieaouq"。

说明:

  • 5 <= word.length <= 2 * 10^5
  • word 仅由小写英文字母组成。
  • 0 <= k <= word.length - 5

思路

求给定字符串 word 的子字符串中至少包含 5 个元音且恰好包含 k 个辅音的子字符串个数。

3305.元音辅音字符串计数I 相比,字符串长度范围变成了 2 * 10^5

恰好型滑动窗口,区间 [left, right] 满足条件不代表 [left, right + 1] 满足条件,可能辅音数量超过了 k,这样就不能累加前面的起点数量,需要重新遍历起点排除一个辅音,退化成暴力解法。

题解提到 恰好 k 个辅音字母 = 至少 k 个辅音字母 - 至少 k + 1 个辅音字母,这样就可以使用两个滑动窗口来求解。

代码


/**
 * @date 2025-03-13 17:03
 */
public class CountOfSubstrings3306 {

    public long countOfSubstrings(String word, int k) {
        return slidingWindow(word, k) - slidingWindow(word, k + 1);
    }

    public long slidingWindow(String word, int k) {
        Set<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u'));
        Map<Character, Integer> map = new HashMap<>();
        int n = word.length();
        int consonantCnt = 0;
        long res = 0;
        int left = 0;
        for (int right = 0; right < n; right++) {
            char c = word.charAt(right);
            if (vowels.contains(c)) {
                Integer cnt = map.getOrDefault(c, 0);
                map.put(c, cnt + 1);
            } else {
                consonantCnt++;
            }
            while (left <= right && consonantCnt >= k && map.size() == 5) {
                c = word.charAt(left++);
                if (vowels.contains(c)) {
                    map.merge(c, -1, Integer::sum);
                    if (map.get(c) == 0) {
                        map.remove(c);
                    }
                } else {
                    consonantCnt--;
                }

            }
            res += left;
        }
        return res;
    }
}

性能

3305.元音辅音字符串计数I

目标

给你一个字符串 word 和一个 非负 整数 k。

返回 word 的 子字符串 中,每个元音字母('a'、'e'、'i'、'o'、'u')至少 出现一次,并且 恰好 包含 k 个辅音字母的子字符串的总数。

示例 1:

输入:word = "aeioqq", k = 1
输出:0
解释:
不存在包含所有元音字母的子字符串。

示例 2:

输入:word = "aeiou", k = 0
输出:1
解释:
唯一一个包含所有元音字母且不含辅音字母的子字符串是 word[0..4],即 "aeiou"。

示例 3:

输入:word = "ieaouqqieaouqq", k = 1
输出:3
解释:
包含所有元音字母并且恰好含有一个辅音字母的子字符串有:
word[0..5],即 "ieaouq"。
word[6..11],即 "qieaou"。
word[7..12],即 "ieaouq"。

说明:

  • 5 <= word.length <= 250
  • word 仅由小写英文字母组成。
  • 0 <= k <= word.length - 5

思路

求给定字符串 word 的子字符串中至少包含 5 个元音且恰好包含 k 个辅音的子字符串个数。

暴力枚举子数组。

代码


/**
 * @date 2025-03-12 8:42
 */
public class CountOfSubstrings3305 {

    public int countOfSubstrings(String word, int k) {
        int n = word.length();
        Set<Character> vowel = new HashSet<>();
        Collections.addAll(vowel, 'a', 'e', 'i', 'o', 'u');
        Map<Character, Integer> map = new HashMap<>();
        int consonantCnt = 0;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                char c = word.charAt(j);
                if (vowel.contains(c)) {
                    Integer cnt = map.getOrDefault(c, 0);
                    map.put(c, cnt + 1);
                } else {
                    consonantCnt++;
                }
                if (consonantCnt == k && map.size() == 5) {
                    res++;
                } else if (consonantCnt > k) {
                    break;
                }
            }
            map.clear();
            consonantCnt = 0;
        }
        return res;
    }

}

性能

2012.数组美丽值求和

目标

给你一个下标从 0 开始的整数数组 nums 。对于每个下标 i(1 <= i <= nums.length - 2),nums[i] 的 美丽值 等于:

  • 2,对于所有 0 <= j < i 且 i < k <= nums.length - 1 ,满足 nums[j] < nums[i] < nums[k]
  • 1,如果满足 nums[i - 1] < nums[i] < nums[i + 1] ,且不满足前面的条件
  • 0,如果上述条件全部不满足

返回符合 1 <= i <= nums.length - 2 的所有 nums[i] 的 美丽值的总和 。

示例 1:

输入:nums = [1,2,3]
输出:2
解释:对于每个符合范围 1 <= i <= 1 的下标 i :
- nums[1] 的美丽值等于 2

示例 2:

输入:nums = [2,4,6,4]
输出:1
解释:对于每个符合范围 1 <= i <= 2 的下标 i :
- nums[1] 的美丽值等于 1
- nums[2] 的美丽值等于 0

示例 3:

输入:nums = [3,2,1]
输出:0
解释:对于每个符合范围 1 <= i <= 1 的下标 i :
- nums[1] 的美丽值等于 0

说明:

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

思路

有一个数组 nums,元素的美丽值定义如下:

  • 如果 nums[i] > nums[j] && 0 =< j < i && nums[i] < nums[k] && i < k <= n - 1,美丽值为 2
  • 如果 nums[i - 1] < nums[i] < nums[i + 1],美丽值为 1
  • 否则美丽值为 0

简单来说就是如果元素大于它前面的所有元素值并且小于它后面的所有元素值,美丽值为 2。如果仅仅大于它前面一个元素且小于它后面一个元素,美丽值为 1

首先想到的是使用单调栈维护当前元素后面第一个 小于等于 它的元素下标,保存到 floor[i],如果没有记录为 n;维护当前元素前面第一个 大于等于 它的元素下标,保存到 ceiling[i],如果没有记录为 -1。当 floor[i] == n && ceiling[i] == -1 时,美丽值为 2floor[i] > i + 1 && ceiling[i] < i - 1 时,美丽值为 1

网友题解使用的是维护前缀最大值和后缀最小值,如果当前元素大于前面的最大值且小于后面的最小值,美丽值为 2,否则直接比较前后两个元素,如果满足条件,美丽值为 1

代码


/**
 * @date 2025-03-11 8:48
 */
public class SumOfBeauties2012 {

    /**
     * 前缀最大值 后缀最小值
     */
    public int sumOfBeauties_v1(int[] nums) {
        int n = nums.length;
        int[] prefix = new int[n];
        int[] suffix = new int[n];
        suffix[n - 1] = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            prefix[i] = Math.max(prefix[i - 1], nums[i - 1]);
            suffix[n - 1 - i] = Math.min(suffix[n - i], nums[n - i]);
        }
        int res = 0;
        for (int i = 1; i < n - 1; i++) {
            if (nums[i] > prefix[i] && nums[i] < suffix[i]) {
                res += 2;
            } else if (nums[i] > nums[i - 1] && nums[i] < nums[i + 1]) {
                res++;
            }
        }
        return res;
    }

    /**
     * 单调栈
     */
    public int sumOfBeauties(int[] nums) {
        int n = nums.length;
        int[] floor = new int[n];
        int[] ceiling = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();
        int res = 0;
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
                floor[stack.pop()] = i;
            }
            stack.push(i);
        }
        while (!stack.isEmpty()) {
            floor[stack.pop()] = n;
        }
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
                ceiling[stack.pop()] = i;
            }
            stack.push(i);
        }
        while (!stack.isEmpty()) {
            ceiling[stack.pop()] = -1;
        }
        for (int i = 1; i < n - 1; i++) {
            if (floor[i] == n && ceiling[i] == -1) {
                res += 2;
            } else if (floor[i] > i + 1 && ceiling[i] < i - 1) {
                res++;
            }
        }
        return res;
    }

}

性能

2070.每一个查询的最大美丽值

目标

给你一个二维整数数组 items ,其中 items[i] = [pricei, beautyi] 分别表示每一个物品的 价格 和 美丽值 。

同时给你一个下标从 0 开始的整数数组 queries 。对于每个查询 queries[j] ,你想求出价格小于等于 queries[j] 的物品中,最大的美丽值 是多少。如果不存在符合条件的物品,那么查询的结果为 0 。

请你返回一个长度与 queries 相同的数组 answer,其中 answer[j]是第 j 个查询的答案。

示例 1:

输入:items = [[1,2],[3,2],[2,4],[5,6],[3,5]], queries = [1,2,3,4,5,6]
输出:[2,4,5,5,6,6]
解释:
- queries[0]=1 ,[1,2] 是唯一价格 <= 1 的物品。所以这个查询的答案为 2 。
- queries[1]=2 ,符合条件的物品有 [1,2] 和 [2,4] 。
  它们中的最大美丽值为 4 。
- queries[2]=3 和 queries[3]=4 ,符合条件的物品都为 [1,2] ,[3,2] ,[2,4] 和 [3,5] 。
  它们中的最大美丽值为 5 。
- queries[4]=5 和 queries[5]=6 ,所有物品都符合条件。
  所以,答案为所有物品中的最大美丽值,为 6 。

示例 2:

输入:items = [[1,2],[1,2],[1,3],[1,4]], queries = [1]
输出:[4]
解释:
每个物品的价格均为 1 ,所以我们选择最大美丽值 4 。
注意,多个物品可能有相同的价格和美丽值。

示例 3:

输入:items = [[10,1000]], queries = [5]
输出:[0]
解释:
没有物品的价格小于等于 5 ,所以没有物品可以选择。
因此,查询的结果为 0 。

说明:

  • 1 <= items.length, queries.length <= 10^5
  • items[i].length == 2
  • 1 <= pricei, beautyi, queries[j] <= 10^9

思路

有一个二维数组 items,其元素 items[i] = [pricei, beautyi] 表示 item 的价格与美丽值。有一个查询数组,每一次查询的目标是找出价格小于等于 queries[i] 的物品中的最大美丽值。

为了求得答案,需要知道小于 queries[i] 都有哪些物品,然后从中找出最大美丽值。

先将 items 按照价格从小到大排序,二分查找 queries[i] 的上界下标 end。剩下的问题是找出 [0, end] 范围内的最大美丽值,这些值是固定的,可以预处理。

代码


/**
 * @date 2025-03-09 22:44
 */
public class MaximumBeauty2070 {

    public int[] maximumBeauty(int[][] items, int[] queries) {
        int n = items.length;
        Arrays.sort(items, (a, b) -> a[0] - b[0]);
        int[] maxBeauty = new int[n];
        maxBeauty[0] = items[0][1];
        for (int i = 1; i < n; i++) {
            maxBeauty[i] = Math.max(maxBeauty[i - 1], items[i][1]);
        }
        int[] res = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            int upperbound = bs(queries[i], items);
            if (upperbound >= 0 && upperbound < n) {
                res[i] = maxBeauty[upperbound];
            }
        }
        return res;
    }

    public int bs(int target, int[][] items) {
        int n = items.length;
        int left = 0, right = n - 1;
        int mid = left + (right - left) / 2;
        while (left <= right) {
            if (items[mid][0] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
            mid = left + (right - left) / 2;
        }
        return right;
    }

}

性能

2597.美丽子集的数目

目标

给你一个由正整数组成的数组 nums 和一个 正 整数 k 。

如果 nums 的子集中,任意两个整数的绝对差均不等于 k ,则认为该子数组是一个 美丽 子集。

返回数组 nums 中 非空 且 美丽 的子集数目。

nums 的子集定义为:可以经由 nums 删除某些元素(也可能不删除)得到的一个数组。只有在删除元素时选择的索引不同的情况下,两个子集才会被视作是不同的子集。

示例 1:

输入:nums = [2,4,6], k = 2
输出:4
解释:数组 nums 中的美丽子集有:[2], [4], [6], [2, 6] 。
可以证明数组 [2,4,6] 中只存在 4 个美丽子集。

示例 2:

输入:nums = [1], k = 1
输出:1
解释:数组 nums 中的美丽数组有:[1] 。
可以证明数组 [1] 中只存在 1 个美丽子集。

说明:

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

思路

返回数组的美丽子序列个数,美丽指子序列中任意两个元素的差的绝对值不等于 k

由于数组长度不大,可以回溯枚举子序列,并针对每一子序列,两两比较,判断差的绝对值是否等于 k

更好的处理方法是使用哈希表记录元素的出现次数,回溯枚举子序列时直接判断当前元素加减 k 的绝对值是否存在于哈希表中。

代码


/**
 * @date 2025-03-07 0:09
 */
public class BeautifulSubsets2597 {

    public int beautifulSubsets(int[] nums, int k) {
        return dfs(0, new ArrayList<>(), nums, k);
    }

    public int dfs(int index, List<Integer> path, int[] nums, int k) {
        if (index == nums.length) {
            return path.size() > 0 ? 1 : 0;
        }
        int res = 0;
        res += dfs(index + 1, path, nums, k);
        for (Integer num : path) {
            if (Math.abs(num - nums[index]) == k) {
                return res;
            }
        }
        path.add(nums[index]);
        res += dfs(index + 1, path, nums, k);
        path.remove(path.size() - 1);
        return res;
    }

}

性能

2588.统计美丽子数组数目

目标

给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:

  • 选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
  • 选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
  • 将 nums[i] 和 nums[j] 都减去 2^k 。

如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。

请你返回数组 nums 中 美丽子数组 的数目。

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

示例 1:

输入:nums = [4,3,1,2,4]
输出:2
解释:nums 中有 2 个美丽子数组:[3,1,2] 和 [4,3,1,2,4] 。
- 按照下述步骤,我们可以将子数组 [3,1,2] 中所有元素变成 0 :
  - 选择 [3, 1, 2] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [1, 1, 0] 。
  - 选择 [1, 1, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 0, 0] 。
- 按照下述步骤,我们可以将子数组 [4,3,1,2,4] 中所有元素变成 0 :
  - 选择 [4, 3, 1, 2, 4] 和 k = 2 。将 2 个数字都减去 22 ,子数组变成 [0, 3, 1, 2, 0] 。
  - 选择 [0, 3, 1, 2, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 2, 0, 2, 0] 。
  - 选择 [0, 2, 0, 2, 0] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [0, 0, 0, 0, 0] 。

示例 2:

输入:nums = [1,10,4]
输出:0
解释:nums 中没有任何美丽子数组。

说明:

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

思路

有一个下标从 0 开始的数组 nums,每次操作可以任选两个不同下标的元素,如果它们在二进制下的第 k 位是 1,那么将这两个元素减去 2^k,即把这两个元素的第 k 位置 0。如果对 nums 的子数组执行任意次操作后可以将子数组所有元素变为 0,称该子数组为 美丽子数组。返回数组 nums 的美丽子数组数目。

通过分析可以知道,累加美丽子数组中所有元素在二进制下每个bit位上 1 的个数,可以发现每个位置上 1 的个数都是偶数。可以通过异或运算来判断是否是美丽子数组。

代码


/**
 * @date 2025-03-06 8:37
 */
public class BeautifulSubarrays2588 {

    public long beautifulSubarrays(int[] nums) {
        int n = nums.length;
        int[] prefix = new int[n + 1];
        Map<Integer, List<Integer>> map = new HashMap<>();
        map.computeIfAbsent(prefix[0], x -> new ArrayList<>()).add(0);
        for (int i = 1; i <= n; i++) {
            prefix[i] = nums[i - 1] ^ prefix[i - 1];
            map.computeIfAbsent(prefix[i], x -> new ArrayList<>()).add(i);
        }
        long res = 0;
        for (List<Integer> value : map.values()) {
            int size = value.size();
            res += (size - 1L) * size / 2;
        }
        return res;
    }

}

性能

1328.破坏回文串

目标

给你一个由小写英文字母组成的回文字符串 palindrome ,请你将其中 一个 字符用任意小写英文字母替换,使得结果字符串的 字典序最小 ,且 不是 回文串。

请你返回结果字符串。如果无法做到,则返回一个 空串 。

如果两个字符串长度相同,那么字符串 a 字典序比字符串 b 小可以这样定义:在 a 和 b 出现不同的第一个位置上,字符串 a 中的字符严格小于 b 中的对应字符。例如,"abcc” 字典序比 "abcd" 小,因为不同的第一个位置是在第四个字符,显然 'c' 比 'd' 小。

示例 1:

输入:palindrome = "abccba"
输出:"aaccba"
解释:存在多种方法可以使 "abccba" 不是回文,例如 "zbccba", "aaccba", 和 "abacba" 。
在所有方法中,"aaccba" 的字典序最小。

示例 2:

输入:palindrome = "a"
输出:""
解释:不存在替换一个字符使 "a" 变成非回文的方法,所以返回空字符串。

说明:

  • 1 <= palindrome.length <= 1000
  • palindrome 只包含小写英文字母。

思路

有一个回文字符串,可以修改其中一个字符使其不是回文,求修改后字典序最小的字符串。如果无法破会回文串,返回空串。

根据题意,字符串长度为 1 直接返回空串。

要使字典序最小,只需使字符串最左边的字符为 a,但是需考虑能否破坏回文串。于是就有了贪心解法,从左到右将对应位置上的字符修改为 a,判断是否是回文,不是回文直接返回。如果均为 a,将最后一个字符替换为 b

注意不要改中间的字符,它不会破坏回文串。

代码


/**
 * @date 2025-03-05 0:06
 */
public class BreakPalindrome1328 {

    public String breakPalindrome(String palindrome) {
        int n = palindrome.length();
        if (n == 1) {
            return "";
        }
        int l = 0, r = n - 1;
        char[] chars = palindrome.toCharArray();
        while (l < r) {
            if (chars[r] != 'a') {
                chars[l] = 'a';
                return new String(chars);
            }
            l++;
            r--;
        }
        chars[n - 1] = 'b';
        return new String(chars);
    }

}

性能