【问题标题】:Longest consecutive sub-sequence with maximal and minimal end points具有最大和最小端点的最长连续子序列
【发布时间】:2014-10-31 23:41:22
【问题描述】:

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

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

【问题讨论】:

  • 你能分享一个例子吗?正如在描述中,您只显示最长而不是最大和最小。
  • 在 [1,2,0,4,7,6,9,3] 中,对于 i=2 和 j=6,ans 将为 4,因为 4,7,6 都大于 0并且小于 9。
  • 但是条件 a[i]

标签: arrays algorithm time-complexity


【解决方案1】:

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

下面的代码计算每个的最长子数组的长度 前缀a[:j + 1]j0n - 1(存储在k)。这 堆栈starts 保存从0j 的索引s,这样 a[s] <= a[t] 代表从 s + 1j 的所有 t,即那些索引 可以在索引j 或之后开始一个有效的子数组。堆栈 blockers 持有从 0j 的索引 s,使得 a[s] > a[t] 对于从s + 1j 的所有t,即最小元素集 需要通过最后一个元素的力排除所有无效子数组 是最大的。 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()

【讨论】:

    【解决方案2】:

    对于每个数字,我们称它为 X。从您处理过的数字中找出最后一个大于 X 的数字。 对于序列 [3, 7, 5, 2, 1, 3, 2, 4],在处理 X = 4 时,最后一个最大的数字是 5,位置 Y = 2(0 索引)。

    Y 可以通过维护一个段树/fenwick 树在 O(log N) 中找到,它回答范围最大查询,树的索引是序列中的值,树的值是序列中的索引.例如:当将值 X 添加到段树时,我们将段树的索引 X 更新为 X 的索引。 要找到 Y,我们只需在树中查询 index > 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)

    编辑: 如果到目前为止没有大于 X 的数字,Y 将默认为 'index -1'。在这种情况下,Z 可以存在于索引 0 直到当前处理的数字之间。 如果 Z 不存在/不存在,则忽略序列中的这个数字并继续下一个。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2019-08-22
    • 2023-03-27
    • 2011-10-03
    • 1970-01-01
    • 2020-04-29
    • 1970-01-01
    相关资源
    最近更新 更多