力扣第241场周赛
题一 找出所有子集的异或总和再求和
描述«
一个数组的 异或总和 定义为数组中所有元素按位 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 遍历所有可能。
代码
public class N1 {
private int sum;
public int subsetXORSum(int[] nums) {
sum=0;
dfs(0,nums,0);
return sum;
}
private void dfs(int pre, int[] nums, int idx) {
if(idx>= nums.length)return;
final int next = pre ^ nums[idx];
sum+=next;
dfs(next,nums,idx+1);
dfs(pre,nums,idx+1);
}
}
题二 构成交替字符串需要的最小交换次数
描述
给你一个二进制字符串 s ,现需要将其转化为一个 交替字符串 。请你计算并返回转化所需的 最小 字符交换次数,如果无法完成转化,返回 -1 。
交替字符串 是指:相邻字符之间不存在相等情况的字符串。例如,字符串 "010" 和 "1010" 属于交替字符串,但 "0100" 不是。
任意两个字符都可以进行交换,不必相邻 。
示例 1:
输入:s = "111000" 输出:1 解释:交换位置 1 和 4:"111000" -> "101010" ,字符串变为交替字符串。 示例 2:
输入:s = "010" 输出:0 解释:字符串已经是交替字符串了,不需要交换。 示例 3:
输入:s = "1110" 输出:-1
提示:
1 <= s.length <= 1000 s[i] 的值为 '0' 或 '1'
思路
- 统计出1和0的个数
- 如果1和0的个数差距大于1 则无法构成交替字符串
- 如果1和0的个数相等:
- 统计偶数位是1需要交替的次数。
- 统计偶数位是0需要交替的次数。
- 取上面的最小值。
- 如果1的个数大于0的个数,则说明偶数位一定是1
- 如果1的个数小于0的个数,则说明偶数位一定是0。
代码
public class N2 {
public int minSwaps(String s) {
final char[] strs = s.toCharArray();
int cnt0,cnt1;
cnt0=cnt1=0;
for (char c:strs){
if(c=='0')cnt0++;
else cnt1++;
}
if(Math.abs(cnt0-cnt1)!=1&&cnt0!=cnt1)return -1;
int res=0;
if(cnt0>cnt1){
for (int i=0;i<strs.length;i+=2){
if(strs[i]=='1')res++;
}
}
else if(cnt0==cnt1){
int res1=0;
int res2=0;
for (int i=0;i<strs.length;i+=2){
if(strs[i]=='0')res1++;
else res2++;
}
res=Math.min(res1,res2);
}
else {
for (int i=0;i<strs.length;i+=2){
if(strs[i]=='0')res++;
}
}
return res;
}
}
题三 找出和为指定值的下标对
描述
给你两个整数数组 nums1 和 nums2 ,请你实现一个支持下述两类查询的数据结构:
累加 ,将一个正整数加到 nums2 中指定下标对应元素上。 计数 ,统计满足 nums1[i] + nums2[j] 等于指定值的下标对 (i, j) 数目(0 <= i < nums1.length 且 0 <= j < nums2.length)。 实现 FindSumPairs 类:
FindSumPairs(int[] nums1, int[] nums2) 使用整数数组 nums1 和 nums2 初始化 FindSumPairs 对象。 void add(int index, int val) 将 val 加到 nums2[index] 上,即,执行 nums2[index] += val 。 int count(int tot) 返回满足 nums1[i] + nums2[j] == tot 的下标对 (i, j) 数目。
示例:
输入: ["FindSumPairs", "count", "add", "count", "count", "add", "add", "count"] [[[1, 1, 2, 2, 2, 3], [1, 4, 5, 2, 5, 4]], [7], [3, 2], [8], [4], [0, 1], [1, 1], [7]] 输出: [null, 8, null, 2, 1, null, null, 11]
解释: FindSumPairs findSumPairs = new FindSumPairs([1, 1, 2, 2, 2, 3], [1, 4, 5, 2, 5, 4]); findSumPairs.count(7); // 返回 8 ; 下标对 (2,2), (3,2), (4,2), (2,4), (3,4), (4,4) 满足 2 + 5 = 7 ,下标对 (5,1), (5,5) 满足 3 + 4 = 7 findSumPairs.add(3, 2); // 此时 nums2 = [1,4,5,4,5,4] findSumPairs.count(8); // 返回 2 ;下标对 (5,2), (5,4) 满足 3 + 5 = 8 findSumPairs.count(4); // 返回 1 ;下标对 (5,0) 满足 3 + 1 = 4 findSumPairs.add(0, 1); // 此时 nums2 = [2,4,5,4,5,4] findSumPairs.add(1, 1); // 此时 nums2 = [2,5,5,4,5,4] findSumPairs.count(7); // 返回 11 ;下标对 (2,1), (2,2), (2,4), (3,1), (3,2), (3,4), (4,1), (4,2), (4,4) 满足 2 + 5 = 7 ,下标对 (5,3), (5,5) 满足 3 + 4 = 7
提示:
1 <= nums1.length <= 1000 1 <= nums2.length <= 10^5 1 <= nums1[i] <= 10^9 1 <= nums2[i] <= 10^5 0 <= index < nums2.length 1 <= val <= 10^5 1 <= tot <= 10^9 最多调用 add 和 count 函数各 1000 次
思路
- 将数据分别额外存储在两个Map中。key是值,value是个数
- add操作的同时更新map2相关KV对
- 求count的是遍历size小的map找出对应map的value,对数相当于两个value相乘。
代码
class FindSumPairs {
private int[] nums2;
private Map<Integer,Integer> map1;
private Map<Integer,Integer> map2;
public FindSumPairs(int[] nums1, int[] nums2) {
this.nums2=nums2;
map1=new HashMap<>();
map2=new HashMap<>();
for (int i:nums1)map1.put(i,map1.getOrDefault(i,0)+1);
for (int i:nums2)map2.put(i,map2.getOrDefault(i,0)+1);
}
public void add(int index, int val) {
final int cnt = map2.getOrDefault(nums2[index], 0) - 1;
if(cnt==0)map2.remove(nums2[index]);
else map2.put(nums2[index],cnt);
nums2[index]+=val;
map2.put(nums2[index],map2.getOrDefault(nums2[index],0)+1);
}
public int count(int tot) {
Map<Integer,Integer> m1;
Map<Integer,Integer> m2;
if(map1.size()<=map2.size()){
m1=map1;
m2=map2;
}else {
m1=map2;
m2=map1;
}
int res=0;
for (Map.Entry<Integer,Integer> e:m1.entrySet()){
res+=e.getValue()*m2.getOrDefault(tot-e.getKey(),0);
}
return res;
}
}
题四 恰有 K 根木棍可以看到的排列数目
描述
有 n 根长度互不相同的木棍,长度为从 1 到 n 的整数。请你将这些木棍排成一排,并满足从左侧 可以看到 恰好 k 根木棍。从左侧 可以看到 木棍的前提是这个木棍的 左侧 不存在比它 更长的 木棍。
例如,如果木棍排列为 [1,3,2,5,4] ,那么从左侧可以看到的就是长度分别为 1、3 、5 的木棍。 给你 n 和 k ,返回符合题目要求的排列 数目 。由于答案可能很大,请返回对 109 + 7 取余 的结果。
示例 1:
输入:n = 3, k = 2 输出:3 解释:[1,3,2], [2,3,1] 和 [2,1,3] 是仅有的能满足恰好 2 根木棍可以看到的排列。 可以看到的木棍已经用粗体+斜体标识。 示例 2:
输入:n = 5, k = 5 输出:1 解释:[1,2,3,4,5] 是唯一一种能满足全部 5 根木棍可以看到的排列。 可以看到的木棍已经用粗体+斜体标识。 示例 3:
输入:n = 20, k = 11 输出:647427950 解释:总共有 647427950 (mod 10^9 + 7) 种能满足恰好有 11 根木棍可以看到的排列。
提示:
1 <= n <= 1000 1 <= k <= n
思路
可以将题转换成n根木棍划分成K个部分。对于每个长为 mm 的部分,对于其任意排列,我们总是可以将排列中最大的元素当作可以看到的木棍,移到该部分的开头,则剩余的木棍可以任意排列,因此每个部分的方案数为 (m-1)!(m−1)!,即为一个长为 mm 的圆排列的方案数。也就是说这是斯特林数。
代码
public class N4 {
private final static int MOD= (int) (Math.pow(10,9)+7);
public int rearrangeSticks(int n, int k) {
long[][] dp=new long[n+1][k+1];
dp[0][0]=1;
for (int i=1;i<=n;i++){
for (int j=1;j<=k;j++){
dp[i][j]=(dp[i-1][j-1]+(i-1)*dp[i-1][j])%MOD;
}
}
return (int) dp[n][k];
}
}