动态规划:最长上升子序列 LCS
最长公共子序列
www.acwing.com/problem/content/899/
DP:
-
状态表示:
f[i][j]
- 集合:所有
A[1~i]
和B[1~j]
的公共子序列的集合 - 属性:长度 max
- 集合:所有
-
状态计算
针对公共子序列的不同增长情况,关注其最后的一个字符
-
包含
A[i]
且包含B[j]
:A[i] == B[j]
-
不变的部分:
A[i]
和B[j]
-
变化的部分:
A[1~i-1]
和B[1~j-1]
f[i][j] = f[i - 1][j - 1] + 1
-
-
不包含
A[i]
但包含B[j]
:-
f[i - 1][j]
包含两种情况:包含B[j]
;不包含B[j]
。所以不能等价 - 然而需要注意,求数量时,应该保证不重不漏;但是求最大值时,只需要保证不漏即可,就算重复了也没关系!
- 所以可以使用
f[i - 1][j]
来覆盖这个子集,即使有重复
-
-
包含
A[i]
但不包含B[j]
:f[i][j - 1]
-
不包含
A[i]
且不包含B[j]
:f[i - 1][j - 1]
- 且这个方案一定被上面两种方案包含,所以也不需要考虑了
-
不漏是关键,不重可以根据所求属性以省去
import java.util.*;
public class Main {
static final int N = 1010;
static int[][] f = new int[N][N];
static char[] a = new char[N];
static char[] b = new char[N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
String input = sc.next();
for (int i = 1; i <= n; i++) {
a[i] = input.charAt(i - 1);
}
input = sc.next();
for (int i = 1; i <= m; i++) {
b[i] = input.charAt(i - 1);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
if (a[i] == b[j]) {
f[i][j] = Math.max(f[i][j], f[i - 1][j - 1] + 1);
}
}
}
System.out.println(f[n][m]);
}
}