Count ones in a segment
难度系数: 4kyu
这个题本来算一个较简单的题目,但提交了几次都显示超时,因此肯定不能使用便利计算,应通过找规律求解!!!规律找了好久才发现,分享给大家。
以下给出最优解(通过,公式计算)和 超时解(未通过,遍历累计)!!!
题目:
Given two numbers: ‘left’ and ‘right’ (1 <= ‘left’ <= ‘right’ <= 200000000000000) return sum of all ‘1’ occurencies in binary representations of numbers between ‘left’ and ‘right’ (including both)
Example:
countOnes 4 7 should return 8, because:
4(dec) = 100(bin), which adds 1 to the result.
5(dec) = 101(bin), which adds 2 to the result.
6(dec) = 110(bin), which adds 2 to the result.
7(dec) = 111(bin), which adds 3 to the result.
So finally result equals 8.
1. 规律求解
通过陈列一部分1的个数可以发现有以下规律:
从0开始,1的个数变化为
[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,....]
可以发现
每2的次方区间内,都是累加之前区间,并对每个元素进行+1,以此类推:如下
[0,1] [1,2] [1,2,2,3],[1,2,2,3,2,3,3,4],...
--> [0,1] [0+1,1+1] [0+1,1+1,1+1,2+1],[0+1,1+1,1+1,2+1,1+1,2+1,2+1,3+1],...
因为
n = 2**p + 2**q +...+ 2**0
继续推理可以发现
要求当前数n的1的个数,只需分块求解即可,并累加当前数 n到求解区间的差值,因为相隔多远就得加多少次 +1。
因此转化为求解2的若干次方内1的个数的累加和
在2的1次方内,sum([0,1])=1 = 2**0
在2的平方内, sum([0,1,1,2]) = 4 = 2**1 + 2**0 + 2**0 = 2*(2**1)
在2的3次方内, sum([0,1,1,2,1,2,2,3]) = 12
= 2**2 + 2*2**1 + 4*2**0
= 3*(2**2)
以此类推规律是不是出来了
在2的p次方内(不包括2**p这个数), sum = p*(2**(p-1))
因此还得加上2**p这个数的1的个数,为1
则有 sum = p*(2**(p-1))+1
又因为 2**p区间距离n的距离为 n-2**p
所以每个区间的累计值为 sum = p*(2**(p-1))+1 + (n-2**p)
接下来就是叠加每个区间的求解值!!!
完整代码为:
def countOnes(left, right):
return count(right) - count(left-1)
def count(n):
s = 0
while n:
p = n.bit_length()-1
p2 = 1<<p
n -= p2
s += p*(p2>>1)+1 + n
print (p,s,n)
return s
顺利通过,言简意赅!!!
一下为未通过,超时解也顺便贴出:
超时解法1:
def countOnes(left, right):
sums = 0
binary = ''
for i in range(left,right+1):
binary += bin(i)[2:]
for j in binary:
if j == '1':
sums += 1
return sums
超时解法2:
def countOnes(left, right):
sums = 0
for i in range(left,right+1):
while i:
if i%2:
sums += 1
i = i//2
return sums
import time
超时解法3:
def countOnes(left, right):
r = right
max_r = 0
while right:
max_r += 1
right = right//2
ini = [0,1]
for i in range(max_r):
ini += [x+1 for x in ini]
sums = sum(ini[left:r+1])
return sums