理解位运算
左移一位相当于乘以2,右移一位相当于除以2
原理
为什么做运算的是补码
Python基础之位运算符(含原码反码补码的通俗解释)_化简可得-CSDN博客
78 子集
for i in range(1 << n):
表示枚举所有状态
if (i >> j) & 1:
tmp.append(nums[j])
表示找到当前状态下,为1的位置,并从nums中找到对应的数字。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
res = []
for i in range(1 << n):
tmp = []
for j in range(n):
if (i >> j) & 1:
tmp.append(nums[j])
res.append(tmp)
return res
作者:edelweisskoko
链接:https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-hui-su-jie-fa-wei-yun-suan-jie-xlryd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5978. 统计追加字母可以获得的单词数
状态压缩
class Solution:
def wordCount(self, startWords: List[str], targetWords: List[str]) -> int:
s = set()
for word in startWords:
mask = 0
for ch in word:
mask |= 1 << (ord(ch) - ord('a'))
s.add(mask)
ans = 0
for word in targetWords:
mask = 0
for ch in word:
mask |= 1 << (ord(ch) - ord('a'))
for ch in word:
if mask ^ (1 << (ord(ch) - ord('a'))) in s: # 去掉这个字符
ans += 1
break
return ans
作者:endlesscheng
链接:https://leetcode-cn.com/problems/count-words-obtained-after-adding-a-letter/solution/ni-xiang-si-wei-wei-yun-suan-ha-xi-biao-l4153/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
对if mask ^ (1 << (ord(ch) - ord('a'))) in s: # 去掉这个字符的理解:
按位异或,如果mask是1 ,那么按位异或之后就是0,即可去掉当前这个字符
class Solution(object):
def wordCount(self, startWords, targetWords):
"""
:type startWords: List[str]
:type targetWords: List[str]
:rtype: int
"""
#把startWords里的单词转化为二进制。
#把targetwords里的单词转化为二进制
#根据异或性质,两位相同为0,不同为1,逐个字母判断去掉之后是否在startWords里
#先把startWords转化为二进制
ans=0
s=set()
for word in startWords:
mask=0
for ch in word:
mask|=1<<(ord(ch)-ord("a"))
s.add(mask)
#把targetwords里的单词转化为二进制
for word in targetWords:
mask=0
for ch in word:
mask|=1<<(ord(ch)-ord("a"))
#判断去掉一个之后,有没有在startword里
for ch in word:
if mask^(1<<(ord(ch)-ord("a"))) in s:
ans+=1
break
return ans
2157. 字符串分组
状态压缩
对于下面这段代码的理解
# 将一个 0 变成 1,或将一个 1 变成 0
# 按位异或,两位不同时为1,相同为0
for i in range(26):
adj.append(mask ^ (1 << i))
按位异或的性质:
1<<i:表示把1左移动i位置,假如当i=2时,1<<2是 100,第三位是1。
假如mask的第三位是1,那么异或之后,第三位0;假如mask的第三位是0,那么异或之后,第三位1 。其余位置和0异或,结果不变。
对下面代码的理解
# 将一个 0 变成 1,且将一个 1 变成 0
for i in range(26):
if mask & (1 << i):
for j in range(26):
if not (mask & (1 << j)):
adj.append(mask ^ (1 << i) ^ (1 << j))
按位与的性质:
mask & (1 << i) 第i位是1
if not (mask & (1 << j)) 第j位不是1(是0)
class Solution:
def groupStrings(self, words: List[str]) -> List[int]:
# 使用哈希映射统计每一个二进制表示出现的次数
wordmasks = Counter()
for word in words:
mask = 0
for ch in word:
mask |= (1 << (ord(ch) - ord("a")))
wordmasks[mask] += 1
# 辅助函数,用来得到 mask 的所有可能的相邻节点
def get_adjacent(mask: int) -> List[int]:
adj = list()
# 将一个 0 变成 1,或将一个 1 变成 0
for i in range(26):
adj.append(mask ^ (1 << i))
# 将一个 0 变成 1,且将一个 1 变成 0
for i in range(26):
if mask & (1 << i):
for j in range(26):
if not (mask & (1 << j)):
adj.append(mask ^ (1 << i) ^ (1 << j))
return adj
used = set()
best = cnt = 0
for mask, occ in wordmasks.items():
if mask in used:
continue
# 从一个未搜索过的节点开始进行广度优先搜索,并求出对应连通分量的大小
q = deque([mask])
used.add(mask)
# total 记录联通分量的大小
total = occ
while q:
u = q.popleft()
for v in get_adjacent(u):
if v in wordmasks and v not in used:
q.append(v)
used.add(v)
total += wordmasks[v]
best = max(best, total)
cnt += 1
return [cnt, best]
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/groups-of-strings/solution/zi-fu-chuan-fen-zu-by-leetcode-solution-a8dr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6007. 数组的最大与和
状态压缩+记忆化深搜
1.为啥要把长度*2
每个篮子可以装2个,长度不*2就只能判断装没装,不知道装几个了
2.state 就是 2 ** (2 * l) - 1,这时候就装满了,全部状态位都是1
class Solution:
def maximumANDSum(self, nums: List[int], numSlots: int) -> int:
l = numSlots
ln = len(nums)
@lru_cache(None)
def dfs(i, state):
if i >= ln:
return 0
n = nums[i]
res = 0
for j in range(2 * l):
#遍历每个二进制位,是0的进行填充。
#可以包括每种情况,因为最开始就是从0到2*l进行遍历的
if state & (1 << j) == 0:
res = max(res, (n & (j // 2 + 1)) + dfs(i + 1, state | (1 << j)))
return res
return dfs(0, 0)
作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/maximum-and-sum-of-array/solution/python-zhuang-ya-dfs-by-qin-qi-shu-hua-2-hnhr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1879. 两个数组最小的异或值之和
状态压缩+记忆化深搜
暴力搜索
不能加 @lru_cache(None),会报错:
class Solution:
def minimumXORSum(self, nums1: List[int], nums2: List[int]) -> int:
# @lru_cache(None)
def dfs(i, used):
if i == len(nums1):
return 0
res = inf
for j in range(len(nums2)):
if j not in used:
used.append(j)
res = min(res, (nums1[i] ^ nums2[j]) + dfs(i + 1, used))
used.pop()
return res
return dfs(0, [])
状压+ dfs
class Solution:
def minimumXORSum(self, nums1: List[int], nums2: List[int]) -> int:
@lru_cache(None)
def dfs(i, state):
if i == len(nums1):
return 0
res = inf
for j in range(len(nums2)):
if state & (1 << j) == 0:
res = min(res, (nums1[i] ^ nums2[j]) + dfs(i + 1, state | (1 << j)))
return res
return dfs(0, 0)
1994 好子集的数目
质数计数+状压
1601. 最多可达成的换楼请求数目
回溯
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
def dfs(i, c):
if i == l:
if degree.count(0) == n:#是否形成多个环
return c
return 0
res = dfs(i + 1, c)#不将i节点加入环中
degree[requests[i][0]] += 1
degree[requests[i][1]] -= 1
res = max(res, dfs(i + 1, c + 1))#将i节点加入寻找最大值,加入后需要将出入度进行恢复
degree[requests[i][0]] -= 1
degree[requests[i][1]] += 1
return res
degree = [0 for i in range(n)]
l = len(requests)
return dfs(0, 0)
作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/solution/python-hui-su-zhuang-ya-shuang-jie-jian-om8tm/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
状压
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
ans = 0
for mask in range(1 << len(requests)):
cnt = mask.bit_count()
if cnt <= ans:
continue
delta = [0] * n
for i, (x, y) in enumerate(requests):
if mask & (1 << i):
delta[x] += 1
delta[y] -= 1
if all(x == 0 for x in delta):
ans = cnt
return ans
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/solution/zui-duo-ke-da-cheng-de-huan-lou-qing-qiu-ae0e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
#二进制遍历每一种情况,找到最大值
res=0
for i in range(1<<len(requests)):
#优化时间
ans=i.bit_count()
if ans<res:
continue
degree=[0 for _ in range(n)]
for j in range(len(requests)):
#每种组合
if i&(1<<j):
degree[requests[j][0]]+=1
degree[requests[j][1]]-=1
#如果degree里都是0,则达到平衡
flag=True
for h in range(n):
if degree[h]!=0:
flag=False
if flag:
res=max(ans,res)
return res
2151 基于陈述统计最多好人数
class Solution:
def maximumGood(self, S: List[List[int]]) -> int:
n,ans=len(S),0
#检查状态是否合理
def check(state):
cnt = 0
#检查每个说真话的人
for i in range(n):
if state>>i & 1:
for j in range(n):
state_j = state>>j & 1
if S[i][j] == 0 and state_j or S[i][j] == 1 and not state_j:
return False,cnt
cnt += 1
return True,cnt
#枚举每个状态
for state in range(1,1<<n):
#如果状态合理,统计一的个数
res,cnt = check(state)
if res:
ans = max(ans,cnt)
return ans
作者:tang-bo-o
链接:https://leetcode-cn.com/problems/maximum-good-people-based-on-statements/solution/mei-tian-yi-dao-kun-nan-ti-di-9tian-ji-y-3azc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
def maximumGood(self, statements: List[List[int]]) -> int:
#二进制枚举,n个玩家里好人的组合,根据statements判断是否符合条件
def check(state):
for j in range(len(statements)):
for h in range(len(statements)):
#如果j是好人,h是好人,statements[j][h]=0不符合
#如果j是好人,h是坏人,statements[j][h]=1不符合
if j!=h:
if state&(1<<j) and state&(1<<h) and statements[j][h]==0:
return False
if state&(1<<j) and not state&(1<<h) and statements[j][h]==1:
return False
return True
res=0
for i in range(1<<len(statements)):
#这个状态是否满足statements
if check(i):
ans=i.bit_count()
res=max(res,ans)
return res
位运算的应用
体验优化,过滤的时候。
面试题 17.01. 不用加号的加法
class Solution {
public int add(int a, int b) {
int x = a ^ b;
int y = (a & b) << 1;
while(y != 0){
int temp = x ^ y ;
y = (x & y) << 1;
x = temp;
}
return x;
}
}
作者:so-what-u
链接:https://leetcode-cn.com/problems/add-without-plus-lcci/solution/di-gui-huo-xun-huan-by-so-what-u-g7yg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
有符号二进制的加法减法
136. 只出现一次的数字
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
a=0
for num in nums:
a=a^num
return a
693 交替位二进制数
宫页题解
6065. 按位与结果大于零的最长组合
二进制模拟
class Solution {
public int largestCombination(int[] candidates) {
int[] cnt = new int[32];
int max = 0;
for (int c : candidates) {
for (int i = 0; i < 32; i++) {
if (((1 << i) & c) > 0) cnt[i]++;
}
}
for (int i = 0; i < 32; i++) {
max = Math.max(max, cnt[i]);
}
return max;
}
}
作者:hu-li-hu-wai
链接:https://leetcode.cn/problems/largest-combination-with-bitwise-and-greater-than-zero/solution/by-hu-li-hu-wai-6kum/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6105. 操作后的最大异或和
可以操作任意次,所以是,可以看成每个数,可以选无数次,像完全背包那样。
每一位有没有出现过1,只要出现过结果就是1。
异或性质:有1个是1,结果就是1.
class Solution(object):
def maximumXOR(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
def maxlen(k):
flag = False
for num in nums:
if num & (1 << k):
flag = True
break
return flag
res = 0
for i in range(32):
# 遍历二进制位
cur = maxlen(i)
if cur:
res |= (1 << i)
return res
268. 丢失的数字
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ans = 0;
for (int i = 0; i < nums.size(); i++)
ans ^= nums[i];
for (int i = 0; i <= nums.size(); i++)
ans ^= i;
return ans;
}
};
作者:bianchengxiong
链接:https://leetcode.cn/problems/missing-number/solution/acmjin-pai-ti-jie-ha-xi-wei-yun-suan-bia-mwtz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6186. 按位或最大的最小子数组长度
位运算+动态规划
class Solution(object):
def smallestSubarrays(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
#从后往前遍历
#用res记录32位每个1的最近出现位置
ans = [0 for _ in range(len(nums))]
res = [0 for _ in range(32)]
for i in range(len(nums)-1,-1,-1):
cur = nums[i]
#当前的nums[i]涵盖了哪些32位
for j in range(32):
if cur & (1 << j):
res[j] = i
need_max = i
for j in range(32):
need_max = max(need_max,res[j])
ans[i] = need_max - i + 1
return ans