algorithm - 找到具有足够平均分数的最长序列

标签 algorithm

我有一个分数介于 0 和 1 之间的长列表。我如何有效地找到所有比 x 元素长的连续子列表,使得每个子列表中的平均分数不小于 y?

例如,如何找到所有长度超过 300 个元素的连续子列表,使得这些子列表的平均分数不低于 0.8?

我主要对满足这些条件的最长子列表感兴趣,实际上并不是所有子列表。所以我正在寻找所有最长的子列表。

最佳答案

如果您只想要最长 这样的子串,这可以在O(n log n) 时间内解决,方法是稍微转换一下问题,然后对最大值进行二分搜索解长度。

让输入的分数列表为 x[1], ..., x[n]。让我们通过从每个元素中减去 y 来转换此列表,以形成列表 z[1], ..., z[n],其元素可以是正数或负数。请注意,任何子列表 x[i .. j] 的平均得分至少为 y 当且仅当 z 中相应子列表中元素的总和(即 z[i] + z[i+ 1] + ... + z[j]) 至少为 0。因此,如果我们有办法有效地计算 z[] 中任何子列表的最大值 总和 T(剧透:我们这样做), 作为副作用,这会告诉我们在 x[] 中是否有 any 子列表平均得分至少为 y:如果 T >= 0 则至少有 1 个这样的子列表,而如果 T < 0 则 x[] 中没有子列表(甚至不是单元素子列表)平均得分至少为 y。但这还没有为我们提供回答您最初问题所需的所有信息,因为没有任何东西强制 z 中的最大和子列表具有最大长度:很可能存在更长的子列表具有较低的总体平均值,但仍具有至少 y 的平均值。

这可以通过概括寻找具有最大总和的子列表的问题来解决:我们现在不是要求总和最大的子列表,而是要求在所有长度至少为 有些给了k。我现在将描述一个算法,给定一个数字列表 z[1], ..., z[n],每个数字都可以是正数或负数,以及任何正整数 k,将计算任何的最大总和z[] 的子列表具有至少 k 的长度,以及达到此和的特定子列表的位置,并且在具有此和的所有子列表中具有最长的可能长度。这是 Kadane's algorithm 的略微概括。 .

FindMaxSumLongerThan(z[], k):
    v = 0                 # Sum of the rightmost k numbers in the current sublist
    For i from 1 to k:
        v = v + z[i]

    best = v
    bestStart = 1
    bestEnd = k

    # Now for each i, with k+1 <= i <= n, find the biggest sum ending at position i.
    tail = -1          # Will contain the maximum sum among all lists ending at i-k
    tailLen = 0        # The length of the longest list having the above sum
    For i from k+1 to n:
        If tail >= 0:
            tail = tail + z[i-k]
            tailLen = tailLen + 1
        Else:
            tail = z[i-k]
            tailLen = 1

        If tail >= 0:
            nonnegTail = tail
            nonnegTailLen = tailLen
        Else:
            nonnegTail = 0
            nonnegTailLen = 0

        v = v + z[i] - z[i-k]    # Slide the window right 1 position
        If v + nonnegTail > best:
            best = v + nonnegTail
            bestStart = i - k - nonnegTailLen + 1
            bestEnd = i

上述算法需要 O(n) 时间和 O(1) 空间,返回 best 中的最大总和以及在 bestStart 中实现该总和的某个子列表的开始和结束位置bestEnd

以上内容有什么用?对于给定的输入列表 x[],假设我们首先通过从每个元素中减去 y 将 x[] 转换为 z[],如上所述;这将是传递给每次调用 FindMaxSumLongerThan() 的 z[]。我们可以将使用 z[] 和给定的最小子列表长度 k 调用函数得到的 best 的值视为 k 的数学函数:best(k)。由于 FindMaxSumLongerThan() 找到长度至少 k 的 z[] 的任何子列表的最大和,best(k) 是 k 的非递增函数。 (假设我们设置 k=5 并发现任何子列表的最大总和为 42;那么如果我们再次尝试使用 k=4 或 k=3,我们保证找到总和至少为 42。)这意味着 我们可以对 k 进行二进制搜索以找到最大的 k 使得 best(k) >= 0:然后 k 将是 x[] 的最长子列表,其平均值至少y。生成的 bestStartbestEnd 将标识具有此属性的特定子列表;在不增加时间复杂度的情况下,很容易修改算法以找到所有这些子列表(最多 n 个,每个最右边的位置一个)。

关于algorithm - 找到具有足够平均分数的最长序列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40558007/

相关文章:

algorithm - 寻找动态规划解决方案

algorithm - 如何实现无间隙 block 布局算法?

algorithm - 如果堆栈操作的时间复杂度为常数 O(1),则该算法的时间复杂度是多少?

c - 查找 int 链表中最常出现的元素的最简单方法

python - 将列表列表替换为 "condensed"列表列表,同时保持顺序

algorithm - 在 25 GB 的语料库中搜索单个单词

c++ - 比较字符串模式的更好解决方案。?

java - 对冒泡排序算法进行正确的运行时分析

二维最近坐标的算法

algorithm - 从轴对齐照片进行 3D 重建