【问题标题】:Longest subarray that has a sum equal to s总和等于 s 的最长子数组
【发布时间】:2021-11-19 07:01:02
【问题描述】:

如何加快执行以下问题陈述的速度?我有一个正确的解决方案,可以通过小输入的每项测试。但是,它超出了较大输入的时间限制。我当前的实现是数组大小的二次方。

问题陈述

您有一个未排序的array arr 非负整数和一个数字s。在arr 中找到一个总和等于s 的最长连续子数组。返回表示其包含边界的两个整数。如果有多个可能的答案,则返回具有最小左界的答案。如果没有答案,返回[-1]。

你的答案应该是从 1 开始的,这意味着数组的第一个位置是 1 而不是 0。

实施

def findLongestSubarrayBySum(s, arr):
    """Return a two-element array that represent the bounds of the subarray."""
    ans = [-1]

    for left in range(len(arr)):
        curr_sum = arr[left]
        if curr_sum == s and len(ans) == 1:
            ans = [left + 1] * 2

        for right in range(left + 1, len(arr)):
            curr_sum += arr[right]

            if curr_sum == s:
                # First found soltion
                if len(ans) == 1:
                    ans = [left + 1, right + 1]
                # Left bound is right bound
                elif ans[1] == ans[0]:
                    ans = [left + 1, right + 1]
                # Longer subarray
                elif ans[1] - ans[0] < right - left:
                    ans = [left + 1, right + 1]

            elif curr_sum > s:
                break

    return ans


if __name__ == '__main__':

    # s = 12  # ans = [2, 4]
    # arr = [1, 2, 3, 7, 5]

    # s = 15  # ans = [1, 5]
    # arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    # s = 15  # ans = [1, 8]
    # arr = [1, 2, 3, 4, 5, 0, 0, 0, 6, 7, 8, 9, 10]

    # s = 3   # ans = -1
    # arr = [1, 1]

    # s = 3   # ans = -1
    # arr = [2]

    # s = 468    # ans = [42, 46]
    # arr = [135, 101, 170, 125, 79, 159, 163, 65, 106, 146, 82, 28,
    #         162, 92, 196, 143, 28, 37, 192, 5, 103, 154, 93, 183, 22,
    #         117, 119, 96, 48, 127, 0, 172, 0, 139, 0, 0, 70, 113, 68,
    #         100, 36, 95, 104, 12, 123, 134]

    # s = 3  # ans = [1, 1]
    # arr = [3]

    # s = 0     # ans = [2, 2]
    # arr = [1, 0, 2]

    s = 3    # ans = [1, 3]
    arr = [0, 3, 0]

    print(findLongestSubarrayBySum(s, arr))

【问题讨论】:

    标签: python arrays algorithm performance time-complexity


    【解决方案1】:

    如果输入包含负数,请在概念上使用前缀和,在实践中使用哈希表。 prefixes_j - prefixes_i = s。对于每个prefixes_j,它只是一个简单的累加和,检查键prefixes_j - s是否存在于哈希表中;之后,如果表中不存在总和,则将其作为指向当前索引的键插入。存储在该键处的值将是第一个看到该前缀和的索引,这样,最左边的索引将返回重复项。

    [1, 2, 3, 7, 5]  s = 12
    
    sum 1
    hash 1
    
    sum 3
    hash 1, 3
    
    sum 6
    hash 1, 3, 6
    
    sum 13
    hash contains 1
    found 13 - 12 = 1
    record length 3
    
    etc...
    

    如果输入不包含负数,请使用这样的想法,即右指针可以在总和超过 s 时立即停止,然后递增左指针直到它再次变小,交替指针以这种方式递增,因此每个指针都遍历列表一共一次。

    【讨论】:

    • 您的第一个解决方案并没有比 OP 更有效地解决问题,因为您必须对每个起始索引 i 重复该过程才能找到最长的子序列给定的列表。
    • @blhsing 完全错误。首先,问题是寻找子数组,而不是子序列。其次,每个操作都是O(1),我们遍历链表一次,加起来是O(n),是最优的。
    【解决方案2】:

    您可以使用两个指针方法,其时间复杂度为O(n),空间复杂度为O(1)。类似的问题和两个指针的解决方法可以找到here

    【讨论】:

    • 如果输入包含负数则不会。
    • 哦,但是它说输入不包含负数,哈!
    • 是的,但在这种情况下,输入仅包括非负整数。
    • 是的,我在我的答案中添加了替代方案。
    【解决方案3】:

    这是一个两指针方法的实现,它在时间上是线性的,在额外的空间开销上是恒定的。它类似于您的解决方案,除了我们不会在每次循环迭代时从大小 0 重新增长窗口。由于您只想要最早、最大的窗口,因此右指针只会增加(而不是增加和减少),从而使时间复杂度易于证明。

    def findLongestSubarrayBySum(s, arr):
        """Return a two-element array that represent the bounds of the subarray."""
        ans = [-1, -2]
        s -= arr[0]
        right = 0
        for left in range(len(arr)):
            while right < len(arr) - 1 and s >= arr[right + 1]:
                s -= arr[right + 1]
                right += 1
            if s == 0 and right - left > ans[-1] - ans[0]:
                ans = [left + 1, right + 1]
            s += arr[left]
        if ans[0] == -1:
            return [-1]
        return ans
    

    【讨论】:

    • 一个以增加右指针开始,然后增加左指针,交替增加的序列是否会导致错误的解决方案?
    • @גלעדברקן 这取决于您的确切意思;您的答案中提出的第二个算法是正确的,并且本质上是它所实现的。我刚刚写了一个实现,因为另一个答案的链接解决了一个不同的问题。
    • 哦,好的。酷,TX!
    猜你喜欢
    • 2020-11-11
    • 2015-01-31
    • 2022-09-14
    • 2017-03-16
    • 2019-12-25
    • 2022-01-05
    • 2014-08-26
    • 2021-11-16
    • 2022-10-12
    相关资源
    最近更新 更多