📌  相关文章
📜  二进制字符串中三元组的计数,使得 S[i]、S[j] 和 S[j]、S[k] 的按位与相同(1)

📅  最后修改于: 2023-12-03 15:06:20.450000             🧑  作者: Mango

二进制字符串中三元组的计数,使得 S[i]、S[j] 和 S[j]、S[k] 的按位与相同

问题描述

给定一个长度为n的二进制字符串S,计算有多少个三元组(i, j, k)满足0≤i<j<k<n,且S[i]、S[j] 和 S[j]、S[k] 的按位与相同。

解决方案
算法思路

题目要求的是三个数的按位与相同,我们可以考虑将一个数拆分成二进制表示下的每一位,然后将这些位拆分成若干个集合,满足一个集合中的所有数字的二进制表示下的1的位置相同。这时,问题就转化成了统计每个集合中的元素之间的三元组个数之和。

如何计算每个集合中的元素的三元组个数呢?考虑将每个集合中的所有元素按位与之后的值进行统计,那么值相同的元素之间构成的三元组的数量就是组合公式C(n, 3)。这里需要注意的是,如果集合中所有元素的二进制表示下的1的数量不足三个,那么对应的组合数为0。同时,如果值相同的元素数量小于三个,那么对应的组合数也为0。

把每个集合内的三元组个数求和即可。

算法实现

首先需要将每个数字的二进制表示下,1的位置拆分成若干个集合,以便于后续统计。这里可以使用哈希表来实现。

def countTriplets(S: str) -> int:
    n = len(S)
    cnt = [collections.Counter() for _ in range(16)]
    ans = 0
    for i in range(n):
        c = int(S[i])
        cnt[c][0] += 1
        for j in range(15):
            if (c >> j) & 1:
                for k in range(16):
                    cnt[k][j+1] += cnt[k][j]
        for j in range(16):
            if cnt[j][j] > 2:
                ans += nCr(cnt[j][j], 3)
        if cnt[0][0] > 2:
            ans += nCr(cnt[0][0], 3)
    return ans

对于每个数,先把二进制表示下为0的位的计数器自增1,然后遍历剩下的每个位,如果为1则将这个位的计数器加到其他位上。

最后统计每个集合中的三元组个数即可。

def nCr(n: int, r: int) -> int:
    if r > n:
        return 0
    if r > n // 2:
        r = n - r
    ans = 1
    for i in range(r):
        ans *= (n - i)
        ans //= (i + 1)
    return ans

组合公式的实现。

总结

本题实际上是基于位运算的一个题目。在实现中,需要考虑到时间复杂度和空间复杂度的限制,因此使用了哈希表和组合公式等数据结构和算法来优化程序性能。