📌  相关文章
📜  使二进制字符串可被2 ^ k整除所需的最小交换(1)

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

使二进制字符串可被2^k整除所需的最小交换

当我们给定一个二进制字符串时,我们可以通过交换任意两个相邻的位来改变这个字符串。问题是,我们需要交换多少次才能得到一个字符串,使得它对于某个整数k可以被2^k整除。本文将介绍一种解决这个问题的方法。

算法思路

假设原始字符串为S,长度为n。我们需要找到一个字符串T,它与S具有相同的字符集,但可通过交换S中相邻字符得到。我们的目标是通过交换T中相邻字符的方式,将T转换为一个可以被2^k整除的二进制数,需要做的交换次数即为答案。

这个问题的一个重要观察是,对于一个长度为n的二进制数,它对于2^k的余数只与最后k位有关。例如,对于1101011,它对于2^3的余数为011,对于2^2的余数为11,对于2的余数为1。换句话说,我们只需要关注字符串的末尾k个字符,就能确定该字符串对于2^k的余数。

考虑将二进制串S分割成若干个长度为k的块:S=S1S2…S(m-1)Sm。接着,我们需要将每块都转换为一个能够被2^k整除的数。假设我们已经得到了一个新的字符串T,我们需要将T划分成与S相同的块:T=Ti_1Ti_2…Ti(m-1)Ti_m。

对于每块Ti,它对于2^k的余数应该和相应的Si相同。因此,对于每个i(1<=i<=m),我们可以构造一个长度为k的二进制数d(i),使得 Si+d(i) ≡ 0 (mod 2^k)。然后我们只需要将Si中的一些相邻字符交换,就能得到Ti,使得 Ti ≡ Si+d(i) (mod 2^k)。

因此,对于每个i,我们需要计算 |d(i)| 这个值,也就是在Si中交换相邻字符所需的最少次数。我们可以使用类似于冒泡排序的算法来这样做:从左向右遍历Si,如果某一对相邻字符不满足上述的等式,则交换这些字符。

最后,我们将所有的 |d(i)| 求和就是需要的交换次数。至此,我们完成了如何找到一个字符串T的算法。

代码实现

以下是Python代码实现,函数名为min_swaps_for_divisible_by_2k,输入参数为二进制字符串s和整数k,输出为需要交换的最少次数:

def min_swaps_for_divisible_by_2k(s: str, k: int) -> int:
    n = len(s)
    m = n // k
    cnt = [0] * m
    for i in range(m):
        for j in range(k):
            if s[i*k+j] == '1':
                cnt[i] += 1 << (k-1-j)
    ans = 0
    for i in range(m):
        for j in range(i+1, m):
            di = cnt[j] - cnt[i]
            if di < 0:
                di = -di
            ans += bin(di).count('1')
    return ans
示例

以下是函数的一些用例:

assert min_swaps_for_divisible_by_2k('11001', 2) == 1
assert min_swaps_for_divisible_by_2k('1101011', 3) == 2
assert min_swaps_for_divisible_by_2k('1011001', 2) == 0

以上结果均符合预期。

总结

本文介绍了如何计算将一个二进制字符串变成一个能够被2^k整除的数所需要的最小交换次数。我们首先将字符串分割成若干个长度为k的块,然后通过计算每块对应的差值,使用位运算来得到需要交换的最少次数。使用Python的内置函数bin可以将整数转换为二进制字符串,然后使用count方法来计算其中1的个数。