算法
人们可能会考虑使用二分搜索。我已经实现了以下算法来确定给定非负整数中最长 1 位字符串的长度n
。计算复杂度为 O(· n
),但对于许多值,n
它将接近 O(log 2 ( · n
))。
该算法的步骤如下,但许多读者发现它们更容易通过首先阅读以下部分(“插图”)来理解。
- 设置
longest
为0
。
- 设置等于( )
len
中的位数。n
len = n.bit_length
- 如果
len <= 2
,则返回答案(0
,1
或2
)。
- 确定(或)
mid
中间位的索引。n
mid = len/2
mid = len >> 1
- 设置
ri = mid+1
和li = mid-1
。
- 如果中间位 (
n[mid]
) 的值为零,则转到步骤 10。
- (
n[mid] = 1
达到这一步。)确定最大索引li >= mid
和最小索引ri <= mid
,使得n[i] = 1
所有ri <= i <= li
.
- 设置和。
longest = [longest, ri-li+1].max
_li += 1
ri -= 1
- 如果
li == len
转到步骤 10;否则设置ln
等于由索引li
(最低有效)到len-1
(最高有效)处的位组成的数量。递归设置为n
并ln
转到步骤 2。这将返回数字ln
( cand
) 中最长的 1 位字符串。设置longest = [longest, cand].max
。
- 如果
ri < 0
进行步骤11;否则设置rn
等于由索引0
(最低有效)到ri
(最高有效)处的位组成的数量。递归设置为n
并rn
转到第 2 步。这将返回第一个数字中最长的 1 位字符串rn (
candlonglong ). Set
= [longest, cand].max`。
longest
在递归中返回。
插图
认为
n = 0b1010011011101011110
#=> 341854
然后
len = n.bit_length
#=> 19
mid = len >> 1
#=> 9
n[mid]
#=> 1
longest = 0
我们可以n
如下图
101001101 1 101011110
中间位1
突出的地方。因为它是 a 1
,所以我们向左和向右寻找 1 的序列。我们得到以下。
10100110 111 01011110
当我们找到三个 1 时,我们更新longest
.
longest = [longest, 3].max
#=> [0, 3].max => 3
我们现在必须检查0b10100110
和0b1011110
(第二个数字中的前导零丢失)。
对于左边的数字,我们计算如下。
n = 0b10100110
len = n.bit_length
#=> 8
mid = len >> 1
#=> 4
n[mid]
#=> 0
所以我们有
101 0 0110
(注意n[0]
是最不重要的位)。我们可以排除0b101
和0b110
,因为两者都有3
位,不超过 的当前值longest
,即3
。
现在我们考虑右半部分,
n = 0b1011110
len = n.bit_length
#=> 7
mid = len >> 1
#=> 3
n[mid]
#=>1
所以我们有
101 1 110
作为n[mid] #=> 1
,我们左右查找 1 的字符串并获得
10 1111 0
由于我们发现了一个4
1 的刺,我们设置
longest = [longest, 4].max
#=> [3, 4].max = 4
2
最后我们看到左边 ( ) 和右边 ( )的位数1
都小于3
,所以我们完成了,返回longest #=> 4
。(OP 实际上想要longest - 1 #=> 3
,我们认为这是一个侧面计算。)
代码
def longest_run(n, longest=0)
len = n.bit_length
return [longest, (n & 1) + (n >> 1)].max if len <= 2
mid = len >> 1
ri = mid-1
li = mid+1
if n[mid] == 1
until n[ri] == 0
ri -= 1
end
until n[li] == 0
li += 1
end
cand = li-ri-1
longest = cand if cand > longest
end
if ri >= 0
shift = ri+1
m = n ^ ((n >> shift) << shift)
if m.bit_length > longest
cand = longest_run(m, longest)
longest = cand if cand > longest
end
end
if li <= len - 1
m = n >> li
if m.bit_length > longest
cand = longest_run(m, longest)
longest = cand if cand > longest
end
end
longest
end
例子
longest_run 341854
#=> 4
longest_run 0b1011110011111000000111100011101111011110
#=> 5
longest_run 0b101111001111100000011110001110111111011111
#=> 6
longest_run 2**500_000-1
#=> 500000
longest_run ("10"*100_000).to_i(2)
#=> 1