问题陈述(HackerRank)
你有一个整数列表,最初列表是空的。
您必须处理三种类型的Q操作:
add s:将整数s添加到列表中,注意整数可以更多 列表中的一次
del s:从列表中删除一个整数s的副本,这是有保证的 列表中至少会有一个s副本。
cnt s:计算列表中有多少个整数,以便得到AND s = a,其中AND是按位AND运算符
输入格式
第一行包含整数Q.以下Q行中的每一行 包含操作类型字符串T和整数s。
约束
- 1≤Q≤200000
- 0≤s< 2 16
输出格式
对于每个 cnt s 操作,请在新行中输出答案。
示例输入
7 add 11 cnt 15 add 4 add 0 cnt 6 del 4 cnt 15
示例输出
1 2 2
解释
对于第一行,我们有15 AND 11 = 11所以答案是1
对于第二行,6 AND 0 = 0和6 AND 4 = 4,所以答案是2
对于第三行,已删除4,我们有15 AND 11 = 11和15 AND 0 = 0所以答案是2
我的工作代码:
operations = int(raw_input())
current = 1
lst = []
while current <= operations:
count = 0
input_ = raw_input().split()
operation = input_[0]
num = int(input_[1])
if operation == 'add':
lst.append(num)
elif operation == 'cnt':
for number in lst:
if number & num == number:
count += 1
print(count)
elif operation == 'del':
lst.remove(num)
current += 1
有些优化的解决方案:CodeReview Answer
operations = int(raw_input())
nums = set()
storage = [0] * (2 ** 16)
for _ in xrange(operations):
input_ = raw_input().split()
operation = input_[0]
num = int(input_[1])
if operation == 'add':
nums |= {num}
storage[num] += 1
elif operation == 'cnt':
print(sum(
storage[number]
for number in nums
if (number & num) == number
))
elif operation == 'del':
storage[num] -= 1
我理解上面的代码和所做的优化。
我不理解的代码:
q = input()
def init():
global a
a = [0 for i in range(1000000)]
def add(s):
for i in range(1 << 8):
if (i&s) == 0:
a[s|i] += 1
def delete(s):
for i in range(1 << 8):
if (i&s) == 0:
a[s|i] -= 1
def cnt(s):
res = 0
for i in range(1 << 8):
if (s|(i<<8)) == s:
res += a[s & (~(i<<8))]
return res
init()
for _ in range(q):
c,s = map(str,raw_input().split())
s = int(s)
if c == "add":
add(s)
elif c == "del":
delete(s)
elif c == "cnt":
print cnt(s)
我无法理解此代码的作者如何使用给定的操作使其成为O(256)。
根据排行榜,上面的代码是Python中两个可接受的提交之一。第二个解决方案(非常类似于上面的解决方案):HackerRank Solution
答案 0 :(得分:4)
该计划实际上是O(q)
,其中q
个操作中的每一个都在O(1)
处理,其常数系数为256 = 2^8 = 8 bits
。 O(256)
是一种离合/非正式的说法。
由于s
最多为2^16
,我们可以通过仅存储每个数字的第一个16
位来解决问题。这将是这样的:
def add(s):
a[s & ((1 << 16) - 1)] += 1 # only save the first 16 bits
def delete(s):
a[s & ((1 << 16) - 1)] -= 1 # only remove the first 16 bits
def cnt(s):
res = 0
for i in range(1 << 16): # also O(1), but with a large constant
if i & s == i:
res += a[i]
return res
然后,您将在O(q)
中找到一个解决方案,但其常数因子2^16
大于解决方案中的2^8
。那么我们如何优化上述内容呢?首先,让我们看看每个函数究竟发生了什么。
def add(s):
for i in range(1 << 8): # 1 << 8 is just a fancy way of saying 2 ** 8, or 256
if (i&s) == 0:
a[s|i] += 1
这意味着,对于每个8
位数i
,如果AND
编辑s
为{0}(这意味着两个没有设置1
{1}})共同位),然后我们将s
OR
d保存为该数字(这意味着s
获得i
的设置位并保持自己的位数)。保存在这里意味着增加该索引的计数。
例如,如果二进制为i = 10
:
i = 0000 0000 => 10 & i == 0 => a[0000 0010] = 1
i = 0000 0001 => 10 & i == 0 => a[0000 0011] = 1
i = 0000 0010 => 10 & i != 0
i = 0000 0011 => 10 & i != 0
i = 0000 0100 => 10 & i == 0 => a[0000 0110] = 1
...
a[... **** **1*] = 1
执行此操作后,我们增加了每个数字的计数,如果使用AND
进行10
,则会导致10
。所以基本上,对于每次添加s
,我们预先计算cnt(s)
的答案。但是在某种程度上,不仅s
是预先计算的,而且二进制表示中的值也是相似的值。
此外,我们还计算了未必添加到列表中的数字。但我们稍后会解决这个问题。
def delete(s):
for i in range(1 << 8):
if (i&s) == 0:
a[s|i] -= 1
这与上述相反。
def cnt(s):
res = 0
for i in range(1 << 8):
if (s|(i<<8)) == s:
res += a[s & (~(i<<8))]
return res
在这里,我们需要查看列表p
中有多少个AND
s
p
仍为8
,我们必须通过检查if (s | (i << 8)) == s
位数。
i
由于8
为i << 8
位,i = **** ****
i << 8 = **** **** 0000 0000
将如下所示:
s
使用此OR
8
会将更多位添加到其最后s = 10
位(或者它们将保持不变)。例如,如果10 | (0 << 8) == 10 => true
10 | (1 << 8) == 10 => false
... the rest should actually all be false
,我们将:
a[s & (~(i << 8))]
现在,我们将增加计数:
i = 0
i = 0000 0000
i << 8 = 0000 0000 0000 0000
~(i << 8) = 1111 1111 1111 1111
10 & (~(i << 8)) = 1111 1111 1111 1111 &
10
= 0000 0000 0000 0010
a[0000 0000 0000 0010] = 1
:
add
基本上,这会反转add
中的编码并找到答案。请记住,在8
中,我们没有触及最后s & (~(i << 8))
位。在这里,我们通过执行3
删除最后一位,只留下列表中的数字。
它背后的直觉是你可以在n
操作中传播工作。我不知道如何给你一个精确的扣除。通常,当您必须使用n / 2
位数时,只需使用n / 2
位数和32
运算,就可以对它们进行大量的计数操作。我通常只是捣乱这些公式直到某些东西粘住,或者我放弃并寻找答案。不是很正式,但是。
一旦你有了解决方案,理解它只需要掌握位操作的确切内容。这可能需要一些时间,但如果你有足够的时间来完成它,它将是一个更加精确的过程。
为了更深入地了解其工作原理,我建议您为方法的每个部分添加打印语句,这些语句会准确显示所发生的事情以及一切如何,就像我尝试的那样。然后使用小输入运行程序。
您可以以类似的方式解决的另一个问题是:给定一堆1
位数,快速回答每个位有多少个(O(1)
)位。使用2^16
的常数因子在do
{
try AVAudioSession.sharedInstance().setActive(true)
} catch let err as NSError
{
println("Dim background error")
}
中执行此操作。