每日一题 数组元素的最小非零乘积
给你一个正整数 p
。你有一个下标从 1 开始的数组 nums
,这个数组包含范围 内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:
- 从
nums
中选择两个元素x
和y
。 - 选择
x
中的一位与y
对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。
比方说,如果 x = 1101
且 y = 0011
,交换右边数起第 2
位后,我们得到 x = 1111
和 y = 0001
。
请你算出进行以上操作 任意次 以后,nums
能得到的 最小非零 乘积。将乘积对 取余 后返回。
注意:答案应为取余 之前 的最小值。
解题思路
根据题意,nums数组的长度为2的p次方-1,且下标从1开始,即数组的下标与元素值相同。要将x,y对应的位置交换之后存在实际意义也就意味着需要对应位置分别为“0”和“1”,且其他位置不相同。从1到2的p次方减一也意味着每个二进制数最大位数为p,所有数乘积最小就意味着我们要尽可能多的“1”。在草稿过程中我发现,不管如何交换,各个位置上的“0”和“1”的总数是不变的,且都为“”和“”,而经过转换都可以变为“”个1乘“”个“”再加上“”,答案好像呼之欲出了。
我直接
return (2**p - 2) ** (2 ** (p-1) - 1) * (2 ** p - 1)
嗯,直到p=3都是正常的,p=4之后就不对了
正当我思路又断了的时候,发现原来题目还有还有个结果求余的操作......这个数比较大一开始用不上,结果写出来之后就给忘了....
代码实现
class Solution:
def minNonZeroProduct(self, p: int) -> int:
return (2**p - 2) ** (2 ** (p-1) - 1) * (2 ** p - 1) % (10**9 + 7)
然后发现算到后面竟然超时了!大概是幂运算数字太大了
改一下计算方法就可以了
class Solution:
def minNonZeroProduct(self, p: int) -> int:
mod = 10**9 + 7
return pow(2 ** p - 2, 2 ** (p - 1) - 1, mod) * (2 ** p - 1) % mod
复杂度分析
时间O(p),空间O(1)
快速幂
快速幂,二进制取幂(Binary Exponentiation,也称平方法),是一个在O(log n)的时间内计算的小技巧,而暴力的计算需要O(n)的时间。这个技巧也常常用在非计算的场景,因为它可以应用在任何具有结合律的运算中。
随机一题 最少的后缀翻转次数
给你一个长度为 n
、下标从 0 开始的二进制字符串 target
。你自己有另一个长度为 n
的二进制字符串 s
,最初每一位上都是 0 。你想要让 s
和 target
相等。
在一步操作,你可以选择下标 i
(0 <= i < n
)并翻转在 闭区间 [i, n - 1]
内的所有位。翻转意味着 '0'
变为 '1'
,而 '1'
变为 '0'
。
返回使 s
与 target
相等需要的最少翻转次数。
解题思路
根据题意,我们在执行每次翻转操作时都需要翻转到后缀字符,因此我们只需要从前到后依次翻转就是最小翻转次数了。而只有前后字符不相同时才需要翻转。(官方题例还第一个就挖坑,复杂化你的思路,真doge)
代码实现
class Solution:
def minFlips(self, target: str) -> int:
if target == 0:
return 0
start = '0'
ans = 0
for i in target:
if i != start:
ans += 1
start = i
return ans
复杂度分析
时间O(n),空间O(1)