文章目录
计算类知识总结
常用计算
- 将 n 二进制表示的最低位 1 移除
n & (n-1) - 获取 n 二进制表示的最低位的 1
n & (-n)
其中 -n = ~n + 1 - 获取 n 二进制的第 i 位
(n >> i) & 1 - 将 n 二进制的第 i 位设为1
(n | (1 << i)) - 取模
当 n 为2的幂的时候,可以用位运算符 & 来取代 %
%8 和 &7 是等价的。同理 %16 等价于 &15,%32 等价于 &31 …
Gosper’s Hack (生成 n元集合所有 k 元子集)
例如从 7 个元素中选 4个元素。
某个集合为 0101110 ,下个集合应该是 0110011。
怎么快速进行转换呢?
记 x 为当前集合,则有:
public int next(int x) {
int lb = x & (-x);
int left = x + lb;
int right = (x ^ (x + lb)) / lb >> 2;
return left + right; // 左右两边拼起来,用 left | right 也可以
}
相关的题目可见:【算法总结】——子集型回溯
判断类知识总结
判断一个数是否可以分解成 k 个 2的任意幂次
以 9 = 1001 9 = 1001 9=1001 为例,判断其是否可以分解成 3 个 2的任意幂次
只要 k ∈ [ x . b i t _ c o u n t ( ) , x ] k ∈ [x.bit\_count(), x] k∈[x.bit_count(),x],就可以完成分解。
一些奇技淫巧
字符大小写相互转换
char a = 'a';
System.out.println((char) (a ^ 32)); // 输出 A
原理解释:以 ‘a’ 和 ‘A’ 为例,
65 的补码为 01000001
~90 01011010
97 的补码为 01100001
~122 01111010
32 的补码为 00100000
所以可以看出 大小写之间的 差异就在二进制表示的 32 那一位上,因此 异或 1 取反,就正好大小写转换了。
英文字母表示成 0 ~ 64 之间的数字
‘A’ ~ ‘Z’ 是 65 ~ 90 (1000001~1011010)
‘a’ ~ ‘z’ 是 97 ~ 112(1100001~1110000)
63 的 二进制表示是:111111
将上述范围变成了 1 ~ 26 和 33 ~ 58。
因此可以用 long 类型的 mask 表示英文字母集合。
相关应用可见:【LeetCode每日一题合集】2023.7.24-2023.7.30
表示1~k的全1集合
(2<<k)-2
同理的,后面可以使用(4<<k)-4
表示从2~(2-1+k)的全1集合
例题
LeetCode 5993. 将找到的值乘以 2
https://leetcode-cn.com/problems/keep-multiplying-found-values-by-two/
class Solution {
public int findFinalValue(int[] nums, int original) {
int mask = 0;
for (int num: nums) {
int k = num / original;
if (num % original == 0 && (k & (k - 1)) == 0) { // 倍数是 2 的幂次
mask |= k;
}
}
mask = ~mask; // 取反后,找最低位的 1(lowbit = mask & -mask)
return original * (mask & -mask);
}
}
二进制数mask
记录nums
中含有哪些 original
的 2 幂次倍数。
遍历完 nums
后,我们可以模拟题目的过程,即从mask
的最低位开始,找连续的 2 的幂次倍数,即连续的 1 的个数。
1177. 构建回文串检测
https://leetcode.cn/problems/can-make-palindrome-from-substring/
class Solution {
public List<Boolean> canMakePaliQueries(String s, int[][] queries) {
int n = s.length();
int[] cnt = new int[n + 1];
for (int i = 0; i < n; ++i) {
cnt[i + 1] = cnt[i] ^ (1 << (s.charAt(i) - 'a')); // 前缀数组
}
List<Boolean> res = new ArrayList();
for (int i = 0; i < queries.length; ++i) {
int l = queries[i][0], r = queries[i][1], k = queries[i][2];
int bits = 0, x = cnt[r + 1] ^ cnt[l];
// 计算1的个数
while (x != 0) {
x &= x - 1;
bits++;
}
res.add(bits <= 2 * k + 1);
}
return res;
}
}