最长公共子序列(Longest Common Subsequence,简称LCS)是计算机科学中的一个经典问题,主要涉及两个序列,并寻找这两个序列中最长的共有子序列。这个子序列不需要在原序列中连续,但必须保持元素的相对顺序。LCS问题在多种领域都有应用,如生物信息学中的DNA序列比对、版本控制系统中的文件差异检测等。
解决LCS问题的一个常见方法是使用动态规划。基本思路是创建一个二维数组,其中dp[i][j]
表示字符串1的前i
个字符和字符串2的前j
个字符的最长公共子序列的长度。状态转移方程如下:
- 如果字符串1的第
i
个字符等于字符串2的第j
个字符,则dp[i][j] = dp[i-1][j-1] + 1
; - 否则,
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
。
除了动态规划外,还可以使用分治法和回溯法来解决LCS问题,但动态规划由于其高效性和简洁性而被广泛采用。LCS问题的时间复杂度通常是O(nm),其中n和m分别是两个序列的长度。
在实际应用中,除了求最长公共子序列的长度,有时还需要输出具体的子序列。这可以通过在动态规划的基础上增加额外的记录步骤来实现,即记录每个dp[i][j]
是由哪个状态转移得来的,然后根据这些信息回溯出最长公共子序列。
LCS问题在算法面试中非常常见,是检验候选人对动态规划理解和应用能力的重要题目之一。最长公共子序列(LCS)问题是算法面试中的常见题目,尤其是在大厂的面试中。以下是三道与LCS相关的面试题目,以及相应的Java源码实现。
题目 1:求两个字符串的最长公共子序列长度
描述:
给定两个字符串,求它们的最长公共子序列的长度。
示例:
输入: str1 = "ABCD", str2 = "ACDF"
输出: 3
Java 源码:
public class LongestCommonSubsequenceLength {public int lcsLength(String str1, String str2) {int m = str1.length();int n = str2.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.charAt(i - 1) == str2.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];}public static void main(String[] args) {LongestCommonSubsequenceLength solution = new LongestCommonSubsequenceLength();String str1 = "ABCD";String str2 = "ACDF";int result = solution.lcsLength(str1, str2);System.out.println("Length of LCS: " + result);}
}
题目 2:求两个字符串的最长公共子序列
描述:
给定两个字符串,求它们的最长公共子序列。
示例:
输入: str1 = "ABCD", str2 = "ACDF"
输出: "ACD"
Java 源码:
public class LongestCommonSubsequence {public String lcs(String str1, String str2) {char[][] dp = new char[str1.length() + 1][str2.length() + 1];buildLCSMatrix(str1, str2, dp);return constructLCS(dp);}private void buildLCSMatrix(String str1, String str2, char[][] dp) {for (int i = 0; i <= str1.length(); i++) {for (int j = 0; j <= str2.length(); j++) {if (i == 0 || j == 0) {dp[i][j] = 0;} else if (str1.charAt(i - 1) == str2.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]);}}}}private String constructLCS(char[][] dp) {int i = dp.length - 1;int j = dp[0].length - 1;StringBuilder lcs = new StringBuilder();while (i > 0 && j > 0) {if (dp[i][j] == dp[i - 1][j - 1] + 1) {lcs.insert(0, dp[i - 1][j - 1] + " ");i--;j--;} else if (dp[i][j] == dp[i - 1][j]) {i--;} else {j--;}}return lcs.toString();}public static void main(String[] args) {LongestCommonSubsequence solution = new LongestCommonSubsequence();String str1 = "ABCD";String str2 = "ACDF";String result = solution.lcs(str1, str2);System.out.println("LCS: " + result);}
}
题目 3:求多个字符串的最长公共子序列
描述:
给定多个字符串,求它们的最长公共子序列。
示例:
输入: strings = ["ABCD", "ACDF", "ABCF"]
输出: "ACD"
Java 源码:
import java.util.List;public class LongestCommonSubsequenceMultiple {public String lcsMultiple(List<String> strings) {if (strings == null || strings.isEmpty()) {return "";}String lcs = strings.get(0);for (int i = 1; i < strings.size(); i++) {lcs = lcs(lcs, strings.get(i));}return lcs;}private String lcs(String str1, String str2) {char[][] dp = new char[str1.length() + 1][str2.length() + 1];buildLCSMatrix(str1, str2, dp);return constructLCS(dp);}private void buildLCSMatrix(String str1, String str2, char[][] dp) {for (int i = 0; i <= str1.length(); i++) {for (int j = 0; j <= str2.length(); j++) {if (i == 0 || j == 0) {dp[i][j] = 0;} else if (str1.charAt(i - 1) == str2.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]);}}}}private String constructLCS(char[][] dp) {int i = dp.length - 1;int j = dp[0].length - 1;StringBuilder lcs = new StringBuilder();while (i > 0 && j > 0) {if (dp[i][j] == dp[i - 1][j - 1] + 1) {lcs.insert(0, dp[i - 1][j - 1] + " ");i--;j--;} else if (dp[i][j] == dp[i - 1][j]) {i--;} else {j--;}}return lcs.toString();}public static void main(String[] args) {LongestCommonSubsequenceMultiple solution = new LongestCommonSubsequenceMultiple();List<String> strings = List.of("ABCD", "ACDF", "ABCF");String result = solution.lcsMultiple(strings);System.out.println("LCS: " + result);}
}
这些题目和源码展示了LCS问题的不同变体以及如何在实际编程中解决它们。在面试中,能够根据问题的特点选择合适的算法并实现其解决方案是非常重要的。希望这些示例能够帮助你更好地准备面试!