给定大小为n的数组A,我们需要找到j-i的最大值,使得对于所有k,i
我已经能够通过动态编程在O(n ^ 2)中解决它。有更好的解决方案吗?
答案 0 :(得分:3)
这是一种新的,经过测试的线性时间解决方案。
下面的代码计算每个子代的最长子阵列的长度
从a[:j + 1]
到j
{存储在0
中的n - 1
前缀k
。该
堆栈starts
保存从s
到0
的索引j
从a[s] <= a[t]
到t
的所有s + 1
j
,即那些索引
可以在索引j
或之后开始有效的子数组。堆栈
blockers
保存从s
到0
的索引j
,以便a[s] > a[t]
对于从t
到s + 1
的所有j
,即最小的元素集
需要通过最后一个元素排除所有无效的子数组
最大化。 starts
和blockers
的维护需要摊销
恒定时间,因为每个循环迭代最多附加一个元素。
以索引j
结尾的有效子数组是从某些开始的有效子数组
starts
中的索引大于除j
之外的所有当前拦截器的索引
本身。天真的,我们每次都可以向后扫描以找到最少的
合适的起始指数,但我们可能会平均扫描太多。
输入变量i
,该变量最多将索引保存到starts
最近扫描。通过i + 1
开始下一次扫描,我们确保了这一点
扫描的摊余成本是恒定的,同时仍然扫描所有
子阵列可能长于k
。
import itertools
def longest(a):
k = 0
n = len(a)
starts = []
blockers = []
for j in range(n):
while starts and a[starts[-1]] > a[j]:
del starts[-1]
starts.append(j)
while blockers and a[blockers[-1]] <= a[j]:
del blockers[-1]
if blockers:
i = min(i + 1, len(starts) - 1)
while i > 0 and starts[i - 1] > blockers[-1]:
i -= 1
else:
i = 0
k = max(k, j + 1 - starts[i])
blockers.append(j)
return k
def longest_slow(a):
n = len(a)
for k in range(n, 0, -1):
for i in range(n - k + 1):
b = a[i:i + k]
if b[0] == min(b) and max(b) == b[-1]:
return k
return 0
def test():
for n in range(9):
for a in itertools.permutations(range(n)):
assert longest(a) == longest_slow(a)
test()
答案 1 :(得分:2)
对于每个号码,我们称之为X.从已处理的号码中找出大于X的最后一个号码。 对于序列[3,7,5,2,1,3,2,4],当您处理X = 4时,最大的最后一个数字是5,位置Y = 2(0索引)。
Y可以在O(log N)中找到,方法是维护一个段树/ fenwick树,该树响应范围最大查询,其中树的索引是序列中的值,树的值是序列中的索引。例如:当将值X添加到段树时,我们更新索引为X的段树的索引X. 为了找到Y,我们只需查询树的最大范围,其中索引&gt; X
现在我们需要找到索引Y和X索引之间的最小数量的索引。这可以使用另一个处理原始序列上的范围最小查询的段树/稀疏表来完成。如果有多个最小数字,请计算索引最低的数字。让我们调用索引Z.这也需要O(log N)。
通过对序列中的每个数字进行这些处理,答案应该是(X的索引)-Z的最大值,从而产生O(N log N)的总体复杂度。
对于由nikhil_vyas提供的案件[-1000,1000,0,1,2,3,4], 当处理最后一个数字时,X = 4,Y将为1(值1000),Z应该在索引1和6之间,它是索引2.因此答案是(索引4)-2 = 6-2 = 4.(i = 2,j = 6)
编辑: Y将默认为&#39; index -1&#39;如果到目前为止没有大于X的数字。在这种情况下,Z可以存在于索引0之间直到当前处理的数字。 如果Z不存在/不存在,请忽略序列中的这个数字,然后转到下一个。