在网上找到以下内容:
你有一个数组 0和1,你想输出所有的间隔(i,j) 0的数量和1的数量相等。示例
pos = 0 1 2 3 4 5 6 7 8 0 1 0 0 1 1 1 1 0
一个间隔是(0,1)因为有数字 0和1相等。还有许多其他间隔,找到所有这些间隔 在线性时间。
我认为没有线性时间算法,因为可能有n ^ 2个这样的间隔。 我对吗?我怎样才能证明有n ^ 2这样的?
答案 0 :(得分:8)
这是我能想到的最快的方式,它与间隔的数量呈线性关系。
设L为原始数字列表,A为空数组的哈希值,其中最初为A [0] = [0]
sum = 0
for i in 0..n
if L[i] == 0:
sum--
A[sum].push(i)
elif L[i] == 1:
sum++
A[sum].push(i)
现在A本质上是序列总和的x y图(x是列表的索引,y是总和)。每次有两个x值x1和x2到y值时,你有一个间隔(x1,x2),其中0和1的数量是相等的。
有m(m-1)/ 2(1到m-1的算术和)间隔,其中A中每个数组M的和为0,其中m = M.length
使用您的示例手动计算A我们使用此图表
L # 0 1 0 1 0 0 1 1 1 1 0
A keys 0 -1 0 -1 0 -1 -2 -1 0 1 2 1
L index -1 0 1 2 3 4 5 6 7 8 9 10
(我添加了#来表示列表的开头,键为-1。同时删除了所有不是0或1的数字,因为它们只是分散注意力)A看起来像这样:
[-2]->[5]
[-1]->[0, 2, 4, 6]
[0]->[-1, 1, 3, 7]
[1]->[8, 10]
[2]->[9]
对于任何M = [a1,a2,a3,...],(ai + 1,aj),其中j> 1。我将是一个与1相同数量的0的区间。例如,在[-1] - > [0,2,4,6]中,间隔为(1,2),(1,4),(1,6),(3,4),(3) ,6),(5,6)。
构建数组A是O(n),但是从A打印这些间隔必须在线性时间内完成间隔数。事实上,这可能是你的证据,即在n的线性时间内完成此操作是不可能的,因为它可能有比n更多的间隔,并且你至少需要间隔迭代次数来打印它们。
除非您认为构建A足以找到所有区间(因为它从A的区间显而易见),然后它与n:P
呈线性关系答案 1 :(得分:2)
线性解决方案是可能的(对不起,早些时候我认为这必须是n ^ 2)如果你小心不打算实际打印结果!
首先,让我们为任意一组零和一组定义一个“得分”,即1的数量减去零的数量。所以(0,1)得分为0,而(0)为-1,(1,1)为2。
现在,从右边开始。如果最右边的数字是0,那么它可以与左边任何得分为1的组合。所以我们需要知道左边有哪些组可用,按分数索引。这表明递归过程会累积具有分数的组。扫描过程为O(n),并且在每个步骤中,过程必须检查它是否已创建新组并扩展已知组的表。检查新组是常量时间(在哈希表中查找)。扩展已知组的表也是恒定时间(起初我认为它不是,但你可以保持一个单独的偏移量,避免更新表中的每个条目)。
所以我们有一个特殊的情况:过程的每一步都确定了一组大小为O(n)的结果,但这样做所需的计算是恒定时间(在该步骤内)。因此,过程本身仍然是O(n)(与步数成比例)。当然,实际打印结果(在步骤期间或结束时)会产生O(n ^ 2)。
我会编写一些Python代码来测试/演示。
我们走了:
SCORE = [-1,1]
class Accumulator:
def __init__(self):
self.offset = 0
self.groups_to_right = {} # map from score to start index
self.even_groups = []
self.index = 0
def append(self, digit):
score = SCORE[digit]
# want existing groups at -score, to sum to zero
# but there's an offset to correct for, so we really want
# groups at -(score+offset)
corrected = -(score + self.offset)
if corrected in self.groups_to_right:
# if this were a linked list we could save a reference
# to the current value. it's not, so we need to filter
# on printing (see below)
self.even_groups.append(
(self.index, self.groups_to_right[corrected]))
# this updates all the known groups
self.offset += score
# this adds the new one, which should be at the index so that
# index + offset = score (so index = score - offset)
groups = self.groups_to_right.get(score-self.offset, [])
groups.append(self.index)
self.groups_to_right[score-self.offset] = groups
# and move on
self.index += 1
#print self.offset
#print self.groups_to_right
#print self.even_groups
#print self.index
def dump(self):
# printing the results does take longer, of course...
for (end, starts) in self.even_groups:
for start in starts:
# this discards the extra points that were added
# to the data after we added it to the results
# (avoidable with linked lists)
if start < end:
print (start, end)
@staticmethod
def run(input):
accumulator = Accumulator()
print input
for digit in input:
accumulator.append(digit)
accumulator.dump()
print
Accumulator.run([0,1,0,0,1,1,1,1,0])
输出:
dynamic: python dynamic.py
[0, 1, 0, 0, 1, 1, 1, 1, 0]
(0, 1)
(1, 2)
(1, 4)
(3, 4)
(0, 5)
(2, 5)
(7, 8)
您可能会担心在显示结果的转储例程中完成一些额外的处理(start < end
的过滤)。但那是因为我正在研究Python缺少链表(我希望扩展一个列表并在固定时间内保存以前的值)。
结果是 size O(n ^ 2)似乎令人惊讶,而找到结果的过程是O(n),但它很容易看看这是怎么可能的:在一个“步骤”,该过程通过关联当前点(self.index
中的append
或end
来识别多个组(大小为O(n)) dump()
)中包含终点列表(self.groups_to_right[...]
或ends
)。
更新:还有一点。 “右边的组”表将具有sqrt(n)条目的“典型宽度”(这取决于中心极限定理 - 它基本上是1D中的随机游走)。由于在每一步添加了一个条目,因此平均长度也是sqrt(n)(在sqrt(n)个bin上共享的n个值)。这意味着,如果包括打印结果,此算法的预期时间(即使用随机输入)为O(n ^ 3/2),即使最坏情况为O(n ^ 2)
答案 2 :(得分:1)
直接回答问题:
你必须构建一个超过O(N)匹配的例子:
让N采用2 ^ k的形式,输入如下:
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 (here, N=16)
匹配次数(其中0是起始字符):
length #
2 N/2
4 N/2 - 1
6 N/2 - 2
8 N/2 - 3
..
N 1
匹配总数(从0开始)是:(1 + N / 2)*(N / 2)/ 2 = N ^ 2/8 + N / 4
从1开始的匹配几乎相同,期望每个长度减少一个。
总计:(N ^ 2/8 + N / 4)* 2 - N / 2 = N ^ 2/4
答案 3 :(得分:0)
每个时间间隔至少包含一个(0,1)
或(1,0)
的序列。因此,只需找到(0,1)
或(1,0)
的每一次出现,然后查看是否与现有解决方案相邻,或者两个书挡元素是否形成另一种解决方案。
通过一些存储技巧,您将能够在线性时间内找到所有解决方案。枚举它们将是O(N ^ 2),但你应该能够在O(N)空间中对它们进行编码。