タイトル説明
https://leetcode-cn.com/problems/longest-common-subsequence/
解決
(1)トップダウン再帰(動的計画法)
class Solution {
// 备忘录,消除重叠子问题
int[][] memo;
/* 主函数 */
int longestCommonSubsequence(String s1, String s2) {
int m = s1.length(), n = s2.length();
// 备忘录值为 -1 代表未曾计算
memo = new int[m][n];
for (int[] row : memo)
Arrays.fill(row, -1);
// 计算 s1[0..] 和 s2[0..] 的 lcs 长度
return dp(s1, 0, s2, 0);
}
// 递归函数,自顶向下的递推,其问题的定义:计算 s1[i..] 和 s2[j..] 的最长公共子序列长度
int dp(String s1, int i, String s2, int j) {
if (i == s1.length() || j == s2.length()) {
return 0;
}
// 如果之前计算过,则直接返回备忘录中的答案
if (memo[i][j] != -1) {
return memo[i][j];
}
// 根据 s1[i] 和 s2[j] 的情况做选择
if (s1.charAt(i) == s2.charAt(j)) {
// s1[i] 和 s2[j] 必然在 lcs 中
memo[i][j] = 1 + dp(s1, i + 1, s2, j + 1);
} else {
// s1[i] 和 s2[j] 至少有一个不在 lcs 中
memo[i][j] = Math.max(
dp(s1, i + 1, s2, j),
dp(s1, i, s2, j + 1)
);
}
return memo[i][j];
}
}
(2)ボトムアップ動的計画法
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
if(text1==null || text2==null) return 0;
int m = text1.length(),n=text2.length();
int [][]dp = new int[m+1][n+1];
//边界
//dp[0][..]表示s1[-1]和s2[...]的最长公共子序
//dp[..][0]表示s2[-1]和s1[...]的最长公共子序
//均为0
//状态递推--严格遵循定义的dp数组 dp[i][j]表示是s1[0,i-1]和s1[0,j-1]的最长公共子序
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(text1.charAt(i-1)==text2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
経験:
- 自分で定義したdp配列の意味に厳密に従って、再帰方程式を導き出します。
- 状態方程式を再帰させるための短い実用的な例を見てください
- トップダウンとボトムアップの動的計画法のdp配列には、反対の意味があります。
- 文字列dp問題は通常、ダブルポインターを使用し、i、jで終わるサブ問題の解決策またはi、jで始まるサブ問題の解決策のいずれかを使用します。