具有最大和最小端点的最长连续子序列

时间:2014-09-07 16:56:08

标签: arrays algorithm time-complexity

给定大小为n的数组A,我们需要找到j-i的最大值,使得对于所有k,i

我已经能够通过动态编程在O(n ^ 2)中解决它。有更好的解决方案吗?

2 个答案:

答案 0 :(得分:3)

这是一种新的,经过测试的线性时间解决方案。

下面的代码计算每个子代的最长子阵列的长度 从a[:j + 1]j {存储在0中的n - 1前缀k。该 堆栈starts保存从s0的索引ja[s] <= a[t]t的所有s + 1 j,即那些索引 可以在索引j或之后开始有效的子数组。堆栈 blockers保存从s0的索引j,以便a[s] > a[t] 对于从ts + 1的所有j,即最小的元素集 需要通过最后一个元素排除所有无效的子数组 最大化。 startsblockers的维护需要摊销 恒定时间,因为每个循环迭代最多附加一个元素。

以索引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不存在/不存在,请忽略序列中的这个数字,然后转到下一个。