📜  仅允许一次插入操作时最长的连续子序列(1)

📅  最后修改于: 2023-12-03 14:49:15.231000             🧑  作者: Mango

仅允许一次插入操作时最长的连续子序列

问题描述

有一个长度为 $n$ 的数组 $a$,现在你有一次操作机会,可以在数组中任意位置插入一个整数。你需要找到在这次操作后,最长的连续子序列的长度。

解法
算法思路

假设插入的整数是 $x$,我们可以用两个数组 $f$ 和 $g$ 分别记录:

  • 在 $a$ 中包含 $x$ 的最长上升子序列长度。
  • 在 $a$ 中包含 $x$ 的最长下降子序列长度。

那么最终的答案就是 $\max\limits_{i=1}^n {f_i+g_i-1}$。其中减一是因为 $x$ 被算了两次。

具体地,则可以利用动态规划的思想从左往右求出 $f$ 数组,然后从右往左求出 $g$ 数组。这两个过程可以分别用时间复杂度为 $O(n\log n)$ 的 LIS(最长上升子序列)算法求解。

代码实现

下面是用 Python 3 实现的代码:

from bisect import bisect_left

def LIS(a):
    """
    返回一个数组的最长上升子序列的长度。
    """
    n = len(a)
    f = [-1] * n  # f[i] 表示以 a[i] 结尾的 LIS 长度。
    prev = [-1] * n  # prev[i] 表示 a[i] 前面的 LIS 中的前驱节点。
    lis = []  # lis[i] 表示长度为 i+1 的 LIS 中的最后一个节点。
    length = 0  # 当前 LIS 的长度。
    for i in range(n):
        j = bisect_left(lis, a[i])
        f[i] = j + 1
        prev[i] = -1 if j == 0 else lis[j-1]
        if j == length:
            lis.append(a[i])
            length += 1
        else:
            lis[j] = min(lis[j], a[i])
    return f

def longest_continuous_subsequence(a):
    """
    返回对数组 a 只能做一次插入操作后最长的连续子序列的长度。
    """
    n = len(a)
    f = LIS(a)
    g = LIS(a[::-1])[::-1]
    return max(f[i]+g[i]-1 for i in range(n))

# 示例:最长连续子序列为 [3,4,5]
a = [1, 3, 5, 2, 6]
print(longest_continuous_subsequence(a))  # 3
时间复杂度

本算法的时间复杂度为 $O(n\log n)$,其中 LIS 算法的时间复杂度为 $O(n\log n)$。