给定一个长度为 N 数组 a 和一个长度为 M 的数组 b。
请你求出它们的最长公共上升子序列长度为多少。
输入第一行包含两个整数 N,M,分别表示数组 a 和 b 的长度。第二行包含 N 个整数 a 1 , a 2 , … , a n a_{1}, a_{2}, \ldots, a_{n} a1,a2,…,an。第三行包含
M 个整数 b 1 , b 2 , … , b n b_{1}, b_{2}, \ldots, b_{n} b1,b2,…,bn。输出一行整数表示答案。
本题为经典的线性动态规划问题。
先回顾一下LIS和LCS:
LIS最长上升子序列:dp[i]
定义为以a[i]
为结尾的最长上升子序列长度。
LCS最长公共子序列:dp[i][j]
定义为数组a
和数组b
的所有公共子序列长度值的最大值。
LCIS最长公共上升子序列:dp[i][j]
定义为数组a
和数组b
的所有公共子序列中以b[j]
结尾的上升子序列长度值的最大值。
首先依据公共子序列中是否包含a[i]
,将dp[i][j]
所代表的集合划分成两个不重不漏的子集:
1.不包含a[i]
的子集,也就是说没有出现公共子集,有没有这个a[i]
不影响当前a[i],b[j]
对应的dp值,则其dp值继承自a[i-1],b[j]
所对应dp值。
则状态转移方程为dp[i][j]=dp[i-1][j]
。
2.包含a[i]
的子集,a[i]=b[j]
即在a[i]
状态出现了一个公共子集,则将这个子集继续划分:即只需要知道在没有a[i]
的上一个状态a[i-1]
时,b[1~j-1]
的最大的dp值+1即可。为了保证单调递增,当前公共子集a[i]=b[j]
必须大于前面任何公共子集,也就是在每一次i
递增中,a[i]
或b[j]
要大于a[i-1]
状态的任一b[1~j-1]
时才能+1。
则状态转移方程为dp[i][j] = max(dp[i][j], dp[i-1][k]+1)
。
红色箭头表示出现公共子集,并计算dp,绿色箭头表示继承自上一个a[i]
状态的dp。注意i=1、2时,并没有出现数字4、5的公共子集,所以它们的dp为0。
void solve(int N, int M) {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++) {
if (a[i] == b[j]) {
for (int k = 0; k < j; k++) {
if (b[k] < b[j]) {
dp[i][j] = max(dp[i][j], dp[i - 1][k] + 1);
}
}
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
}
注意求出的dp[i][j]
是以b[j]
结尾的上升子序列长度值的最大值,所以要得到所有以b[1~j]
结尾的dp值的最大值,需要遍历一下求最大值。
for (int i = 1; i <= M; i++) {
res = max(res, dp[N][i]);
}
本文参考来源:
AcWing 272. 最长公共上升子序列
蓝桥题库LCIS