线性DP
最长上升子序列 LIS#
方法一时间复杂度 O (N^2)#
B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
方法二时间复杂度 O (NlogN)#
用 f[len]
表示 所有长度为 len
的序列最大值(即序列的最后一位)的最小值,因此可知 f
数组呈现严格递增趋势。如果新的 mp[i]
大于 f[len]
则长度为 len 的序列加一,即维护 f[len+1]
的值,使它保持最小。 如何找到这样的 f[len]
呢?因为 f
严格递增,所以用 二分的方法找小于 mp[i]
的最大值(上升子序列,不下降则小于等于),然后对它的下一位也就是 f[len+1]
进行操作。 B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
以最小输入顺序输出最长不下降子序列#
num[i]
相当于数组链表,num[i]
表示以 i
为结尾的序列上一个位置的下标,且 仅在序列增长时更新num[i]
来保证是最小输入顺序
以最小字典序输出最小不下降子序列#
最长公共子序列 LCS#
给定一个长度为 n
的序列 A,和一个长度为 m
的序列 B(n,m<=5000),求出一个最长的序列,使得该序列既是 A 的子序列,也是 B 的子序列。
f[i][j]
表示 A 的前 i
个元素与 B 的前 j
个元素的最长公共子序列,时间复杂度 O (NM)
这段代码首先创建了一个二维数组 dp
来存储两个字符串的最长公共子序列的长度。然后,通过比较两个字符串的字符来填充这个数组。如果字符相同,那么这个位置的值就是左上角的值加一;如果字符不同,那么这个位置的值就是左边和上边的最大值。
最后,从右下角开始回溯,如果当前字符相同,那么就将这个字符加入到结果字符串中,并向左上角移动;如果当前字符不同,那么就向值较大的方向移动。直到回溯到左上角,得到的字符串就是最长公共子序列。
时间复杂度 O (mn),空间复杂度 O (mn)
最长公共上升子序列#
算法竞赛进阶指南 P 241
最长连续公共子序列#
求解最长连续公共子序列(Longest Common Substring, LCSq)的问题可以通过动态规划来解决。这里提供一个 C++的实现示例:
代码解释:#
初始化:我们创建了一个二维数组dp
,其中dp[i][j]
表示str 1
的前i
个字符和str 2
的前j
个字符之间的最长连续公共子序列的长度。所有元素初始化为 0。
动态规划填表:遍历两个字符串,当我们发现两个字符str 1[i-1]
和str 2[j-1]
相等时,我们将dp[i][j]
设置为dp[i-1][j-1] + 1
,表示找到了一个长度更长的连续公共子序列。同时,我们更新最长长度maxLength
和子序列在str 1
中的结束索引endIndex
。
回溯:根据maxLength
和endIndex
,我们可以通过substr
函数直接从str 1
中提取出最长连续公共子序列。
输出:最后,程序打印出找到的最长连续公共子序列。
这种方法能够有效地找到两个字符串之间的最长连续公共子序列,其时间复杂度和空间复杂度均为(O (mn)),其中(m) 和(n) 分别是两个字符串的长度。