6127. 优质数对的数目 (第 303 场力扣周赛)

120 阅读3分钟

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^5
  • 1 <= nums[i] <= 10^9
  • 1 <= 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 位在 num1num2 中是否为 1 的情况:

  1. 若第 i 位的 1 只在 num1num2 中出现一次,则它只会在 num1 OR num2 的结果中出现,对位数之和的贡献是 1

  2. 若第 i 位的 1num1num2 中出现两次,则它会在 num1 OR num2num1 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
}