题目:
给定两个字符串 str1 和 str2 ,返回两个字符串的最长公共子序列。
举例:
str1 = "1A2C3D4B56" str2 = "B1D23CA45B6A"
最长公共子序列为:"123456" 或 "12C4B6" 返回哪个都行
思路:
1. 生成 M*N的矩阵dp
代码实现:
public static int[][] getdp(char[] str1, char[] str2) {
int[][] dp = new int[str1.length][str2.length];
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
//初始化第一列
for (int i = 1; i < str1.length; i++) {
dp[i][0] = Math.max(dp[i-1][0],str1[i] == str2[0] ? 1 : 0);
}
//初始化第一行
for (int j = 1; j < str2.length; j++) {
dp[0][j] = Math.max(dp[0][j-1], str1[0] == str2[j] ? 1 : 0);
}
//填充其他位置
for (int i =1; i < str1.length; i++) {
for (int j = 1; j < str2.length; j++) {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
if (str1[i] == str2[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1]+1);
}
}
}
return dp;
}
测试代码:
public static void main(String[] args) {
char[] str1 = "1A2C3D4B56".toCharArray();
char[] str2 = "B1D23CA45B6A".toCharArray();
int[][] dp = getdp(str1, str2);
System.out.println(Arrays.deepToString(dp));
}
测试结果:
2. 从dp中拿到最长公共子序列
dp 矩阵中最右下角的值代表str1整体和str2整体的最长公共子序列的长度。
从矩阵的右下角开始,有三种移动方式,向上、向左、向右。
(1)如果 dp[i][j] > dp[i][j-1] 和 dp[i-1][j] ,说明之前在计算 dp[i][j] 的时候,选择了 dp[i-1][j-1]+1这个位置。这就说明 str1[i] 等于 str2[j]。这个字符一定属于最长公共子序列,放入 res。
(2)如果 dp[i][j] 等于 dp[i-1][j] 向上移动。
(3)如果 dp[i][j] 等于 dp[i][j-1] 向左移动。
代码实现:
public static String lcse(String str1, String str2) {
if (str1 == null || str2 == null || str1.equals("") || str2.equals("")) {
return "";
}
//将string转换为char数组
char[] chs1 = str1.toCharArray();
char[] chs2 = str2.toCharArray();
int[][] dp = getdp(chs1, chs2);
int M = chs1.length-1;
int N = chs2.length-1;
//拿到 dp矩阵的最右下角的元素
char[] res = new char[dp[M][N]];
//index 从末尾开始
int index = res.length - 1;
while (index >= 0) {
//向左移
if (N > 0 && dp[M][N] == dp[M][N-1]) {
N--;
//向上移
} else if (M > 0 && dp[M][N] == dp[M-1][N]) {
M--;
} else {
res[index] = chs1[M];
index--;
M--;
N--;
}
}
return String.valueOf(res);
}