📜  满足给定条件的最长子序列(1)

📅  最后修改于: 2023-12-03 15:40:47.921000             🧑  作者: Mango

满足给定条件的最长子序列

在进行序列相关问题的思考中,通常需要对于子序列进行操作,因为子序列限制了问题的规模,所以在处理子序列时需要依照具体问题情况进行分析。这里将介绍一种在满足给定条件的情况下找到最长子序列的算法。

问题描述

在一个固定的序列中,找到最长的子序列,这个子序列需要满足以下的条件:

  • 子序列中元素的相对顺序需要和原始序列中的相对顺序相同
  • 子序列中元素的取值不能重复

比如,对于序列1 5 2 3 4,满足条件的最长子序列是1 2 3 4。

算法思路

为了解决这个问题,我们需要同时考虑子序列的长度和子序列中包含的最后一个元素的编号。因为在限制了子序列中元素取值不能重复时,相对顺序中的大小关系无法直接用元素所在位置推断,所以我们需要把问题抽象到数值大小上来得以优化。

因此,我们可以使用动态规划的办法,同时记录以当前位置结尾的最长子序列长度和该长度下子序列中包含的最后一个元素的编号。遍历原始序列,找到以当前元素为结尾的最长子序列长度以及编号信息。这时,我们需要在历史数据中查找是否已有以当前元素所对应的值结尾的子序列信息,如果有,就直接在当前长度基础上加1,否则就从为1的长度开始记录新的子序列信息。

代码实现

我们可以新建一个长度为n的状态数组f[],依次遍历原始序列。

定义f[i],表示以序列中第i个元素结尾的子串中符合条件的最长子序列长度。

同时也需要定义一个map集合,用于存储以元素值为键,以该元素在原始序列中的最后位置为值的记录,用于在后续状态转移时查找。

状态转移方程如下:

$$条件判断:\begin{cases} if\ i == 0: & f[i] = 1 \ if\ seq[i] <= seq[i-1]: & f[i] = f[i-1]\ else & \begin{cases} if\ seq[i] not\ in\ map: & f[i] = f[i-1] + 1;\ map[seq[i]] = i\ \ else: & \begin{cases} if\ i - map[seq[i]] > f[i-1]: & f[i] = f[i-1] + 1;\ map[seq[i]] = i\ \ if\ i - map[seq[i]] < f[i-1]: & f[i] = f[i-1]\ \ if\ i - map[seq[i]] == f[i-1]: & f[i] = f[i-1] + 1;\ map[seq[i]] = i\ \ \end{cases} \end{cases} \end{cases}$$

以下是代码实现,时间复杂度为$O(nlogn)$,空间复杂度为$O(n)$。

def find_longest_subsequence(seq: List[int]) -> int:
    if not seq:
        return 0
    n = len(seq)
    f = [0] * n
    map = {}
    f[0] = 1
    map[seq[0]] = 0

    for i in range(1, n):
        if seq[i] <= seq[i-1]:
            f[i] = f[i-1]
        else:
            if seq[i] not in map:
                f[i] = f[i-1] + 1
                map[seq[i]] = i
            else:
                if i - map[seq[i]] > f[i-1]:
                    f[i] = f[i-1] + 1
                    map[seq[i]] = i
                elif i - map[seq[i]] == f[i-1]:
                    f[i] = f[i-1] + 1
                    map[seq[i]] = i
                else:
                    f[i] = f[i-1]

    return f[-1]
总结

在求解子序列问题时,需要在子序列长度和取值之间平衡好关系。对于本问题,我们利用状态数组和map集合两个结构进行综合考虑,比较容易实现。但在遍历过程中,需要频繁查找map集合中的元素,因此时间复杂度较大。针对于该问题,我们可以使用其他的数据结构,如二叉树、堆、哈希表等进行优化,提高代码效率。