6127. 优质数对的数目
给你一个下标从 0 开始的正整数数组 nums 和一个正整数 k 。
如果满足下述条件,则数对 (num1, num2) 是 优质数对 :
num1和num2都 在数组nums中存在。num1 OR num2和num1 AND num2的二进制表示中值为 1 的位数之和大于等于k,其中OR是按位 或 操作,而AND是按位 与 操作。
返回 不同 优质数对的数目。
如果 a != c 或者 b != d ,则认为 (a, b) 和 (c, d) 是不同的两个数对。例如,(1, 2) 和 (2, 1) 不同。
注意: 如果 num1 在数组中至少出现 一次 ,则满足 num1 == num2 的数对 (num1, num2) 也可以是优质数对。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: 5
解释: 有如下几个优质数对:
- (3, 3):(3 AND 3) 和 (3 OR 3) 的二进制表示都等于 (11) 。值为 1 的位数和等于 2 + 2 = 4 ,大于等于 k = 3 。
- (2, 3) 和 (3, 2): (2 AND 3) 的二进制表示等于 (10) ,(2 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。
- (1, 3) 和 (3, 1): (1 AND 3) 的二进制表示等于 (01) ,(1 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。
所以优质数对的数目是 5 。
示例 2:
输入: nums = [5,1,1], k = 10
输出: 0
解释: 该数组中不存在优质数对。
提示:
1 <= nums.length <= 10^51 <= nums[i] <= 10^91 <= k <= 60
一、模拟解法:超时
func countExcellentPairs(nums []int, k int) int64 {
var ans int64
set := map[int]struct{}{}
for _, num := range nums {
set[num] = struct{}{}
}
for n1,_ := range set {
for n2,_ := range set {
if countOnes(n1,n2) >= k {
ans++
}
}
}
return ans
}
func countOnes(nums1,nums2 int) int{
or := nums1 | nums2
an := nums1 & nums2
res := 0
for ; or > 0; or &= (or-1){
res++
}
for ; an > 0;an &= (an-1){
res++
}
return res
}
二、总结规律
讨论二进制第 i 位在 num1 和 num2 中是否为 1 的情况:
-
若第
i位的1只在num1和num2中出现一次,则它只会在num1 OR num2的结果中出现,对位数之和的贡献是1; -
若第
i位的1在num1和num2中出现两次,则它会在num1 OR num2和num1 AND num2的结果中出现,对位数之和的贡献是2。
也就是说,第 i 位在两个数里出现几次,它的贡献就是几。即,两个数中1的个数是多少,贡献就是多少。因此我们维护 f(x) 表示数 x 中有几个 1,题目变为:
求不同数对 (x,y) 的数量,使得 f(x) + f(y) >= k。
统计答案时,我们枚举 f(x) 和 f(y)。记 g(t) 表示 f(x) = t 的不同 x 有几个。根据数学知识,满足 f(x)+f(y) ≥ k 的数对有 g(f(x)) × g(f(y)) 对。把枚举过程中的所有答案加起来即可。
复杂度O(n)+O(n*logA)+O(logA * logA),其中 A 是数组中的最大元素。
func countExcellentPairs(nums []int, k int) int64 {
var ans int64
cnt := map[int]int64{}
set := map[int]struct{}{}
for _, num := range nums {
set[num] = struct{}{}
}
for n,_ := range set {
cnt[countOnes(n)]++
}
for n1,_ := range cnt {
for n2,_ := range cnt {
if n1+n2 >= k {
ans += cnt[n1]*cnt[n2]
}
}
}
return ans
}
func countOnes(num int) int{
res := 0
for ; num > 0;num &= (num-1){
res++
}
return res
}