LeetCode 1143.Longest Common Subsequence
解法一 递归
分析:假设字符串s1和字符串s2的最长公共子序列为lcs,则若从后往前搜索s1和s2时,若s1[i] == s2[j] 必有这个字符在lcs中。否则,若不在lcs中,那么lcs可以加上这个字符变得更长,不符合最长的定义。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
return dp(text1, m-1, text2, n-1);
}
public static int dp(String s, int i, String t, int j) {
if (i < 0 || j < 0)
return 0;
if (s.charAt(i) == t.charAt(j))
return dp(s, i-1, t, j-1) + 1;
else
return Integer.max(dp(s, i, t, j-1), dp(s, i-1, t, j));
}
}
TLE
解法二 带备忘录的递归
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] memo = new int[m][n];
for (int i = 0; i < m; i++)
Arrays.fill(memo[i], 0);
return dp(text1, m-1, text2, n-1, memo);
}
public static int dp(String s, int i, String t, int j, int[][] memo) {
if (i < 0 || j < 0)
return 0;
if (memo[i][j] != 0)
return memo[i][j];
if (s.charAt(i) == t.charAt(j))
memo[i][j] = dp(s, i-1, t, j-1, memo) + 1;
else
memo[i][j] = Integer.max(dp(s, i, t, j-1, memo), dp(s, i-1, t, j, memo));
return memo[i][j];
}
}
用时: 55ms, 超过6.94%用户
动态规划
分析:最值,最优子结构,重叠子问题,一般字符串的子序列问题都可以用DP
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
for (int j = 0; j <= n; j++)
dp[0][j] = 0;
for (int i = 0; i <= m; i++)
dp[i][0] = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int tmp = text1.charAt(i) == text2.charAt(j) ? 1 : 0;
dp[i+1][j+1] = Math.max(Math.max(dp[i][j+1], dp[i+1][j]), dp[i][j]+tmp);
}
}
return dp[m][n];
}
}
用时: 23ms, 超过6%
动态规划优化
优化考虑:无需将 d p [ i ] [ j ] + 1 dp[i][j]+1 dp[i][j]+1和其他两种情况比较,因为如果text1[i] == text2[j],则必然text1[i]和text2[j]在text1[0…i]和text2[0…j]的lcs中。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
for (int j = 0; j <= n; j++)
dp[0][j] = 0;
for (int i = 0; i <= m; i++)
dp[i][0] = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (text1.charAt(i) == text2.charAt(j))
dp[i+1][j+1] = dp[i][j] + 1;
else
dp[i+1][j+1] = Math.max(dp[i][j+1], dp[i+1][j]);
}
}
return dp[m][n];
}
}
用时: 11ms, 超过77%
动态规划再优化
直接string.charAt()会比较慢,可以先转成char[]数组,加快速度
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] str1 = text1.toCharArray();
char[] str2 = text2.toCharArray();
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i-1] == str2[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];
}
}
用时: 6ms, 超过98.27%