题目链接:1143. 最长公共子序列 - 力扣(LeetCode)
前情提要:
如果你做过718. 最长重复子数组 - 力扣(LeetCode)并且看过我的这篇题解力扣718-最长重复子数组(Java详细题解)-CSDN博客,那么做这道题会简单很多。
因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。
dp五部曲。
1.确定dp数组和i下标的含义。
2.确定递推公式。
3.dp初始化。
4.确定dp的遍历顺序。
5.如果没有ac打印dp数组 利于debug。
每一个dp题目如果都用这五步分析清楚,那么这道题就能解出来了。
题目思路:
本题是要求俩个字符串的最长公共子序列的长度。注意这里的子序列和力扣718. 最长重复子数组的子序列不一样,本题的子序列是可以不连续的,而最长重复子数组是需要连续的。
话不多说,直接开始我们的dp五部曲。
1.确定dp数组和i下标的含义。
dp[i] [j]的含义是以i - 1结尾的字符串与j - 1结尾的字符串最长公共子序列的长度。
至于为什么是i- 1和j - 1大家可以看看这篇力扣718-最长重复子数组(Java详细题解)-CSDN博客。
2.确定递推公式。
本题就难在递推公式。
因为我们要求最长公共子序列,可以是不连续的。
所以主要是俩大情况。一个是nums[i - 1] == nums[j - 1],
另一个就是不等于的情况。
当nums[i - 1] == nums[j - 1]时,说明此时结尾的元素相等,那么此时的最长公共子序列就等于他前一个状态加上相同元素的长度即可.
dp[i] [j] = dp[i - 1] [j - 1] + 1。
当nums[i - 1] != nums[j - 1]时,说明此时结尾的元素不等,那么此时我们就需要删除俩个数组中的最后一位元素。我们需要删除哪一个数组呢?
不知道,但是我们是要求最长的公共子序列的长度,所以我们取一个最大即可。
dp[i] [j] = Math.max(dp[i] [j - 1],dp[i - 1] [j]);
3.dp初始化。
由递推公式可以看出,最长公共子序列不仅由它左上的方向推出还可以由它左和上的方向推出。
所以第一排和第一列是递推的起点,我们就要初始化第一排和第一列。
因为dp[i] [0]和dp[0] [i]是将一个没有意义的字符串和另一个字符串求最长公共子序列,所以他们的长度就为0。
4.确定dp的遍历顺序。
由递推公式可以得出最长公共子序列不仅由它左上的方向推出还可以由它左和上的方向推出。
所以我们的遍历顺序是从左到右,从上到下。
5.如果没有ac打印dp数组 利于debug。
最终代码:
class Solution {public int longestCommonSubsequence(String text1, String text2) {int [][] dp = new int [text1.length() + 1][text2.length() + 1];int result = 0;for(int i = 1;i <= text1.length();i ++){for(int j = 1;j <= text2.length();j ++){if(text1.charAt(i - 1) == text2.charAt(j - 1)){//这里与最长重复子数组有一点区别 因为子数组是一种连续的结构 只要有一个不同 那么就不算重复的//但是子序列不同 只要是在某一个界限前 一样的都算//所以这里有俩种情况 当text1.charAt(i - 1) == text2.charAt(j - 1)时 那么就该把长度加一 俩个前一位的最长的公共子序列然后加1dp[i][j] = dp[i - 1][j - 1] + 1;}//如果不同 也有可能是有公共子序列的 比如 abc 与 ace 虽然最后一位不同 但他们是最长子序列为2//所以我们可以把拿abc以c结尾的字符串和ace以c为结尾字符串做对比//同理也可以拿abc以b为结尾的字符串与ace以e为结尾字符串做对比//抽象就是 最长公共子序列不仅由它左上的方向推出还可以由它左和上的方向推出else{dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - 1]);}text1.length()//这里顺便记录一下最长公共子序列的最大长度 其实也可以不用记录 返回dp[text1.length()][text2.length()]也可以//因为本题所有的dp数组都会更新result = Math.max(result,dp[i][j]);}}return result;}
}
这一篇博客就到这了,如果你有什么疑问和想法可以打在评论区,或者私信我。
我很乐意为你解答。那么我们下篇再见!