Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
"ace" is a subsequence of "abcde".A common subsequence of two strings is a subsequence that is common to both strings.
Example 1:
Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace" with length 3.
Example 2:
Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" with length 3.
Example 3:
Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no common subsequence.
The LCS problem has optimal substructure and overlapping subproblems, making it perfect for dynamic programming.
Key Insight:
Create a 2D DP table of size (m+1) × (n+1) where:
m = length of text1n = length of text2dp[i][j] = LCS length of text1[0..i-1] and text2[0..j-1]Initialize base cases:
dp[i][0] = 0 (empty text2)dp[0][j] = 0 (empty text1)Fill the table using the recurrence:
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
Return dp[m][n]
This problem demonstrates:
dp[i][j] uses text1[i-1] and text2[j-1]1class Solution {
2 // Solution 1: 2D Dynamic Programming (Bottom-up)
3 public int longestCommonSubsequence(String text1, String text2) {
4 int m = text1.length();
5 int n = text2.length();
6
7 // Create DP table
8 int[][] dp = new int[m + 1][n + 1];
9
10 // Fill DP table
11 for (int i = 1; i <= m; i++) {
12 for (int j = 1; j <= n; j++) {
13 if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
14 // Characters match: take diagonal + 1
15 dp[i][j] = dp[i - 1][j - 1] + 1;
16 } else {
17 // No match: take max of top or left
18 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
19 }
20 }
21 }
22
23 return dp[m][n];
24 }
25
26 // Solution 2: Space-Optimized DP (1D array)
27 public int longestCommonSubsequenceOptimized(String text1, String text2) {
28 // Make text2 the shorter string
29 if (text1.length() < text2.length()) {
30 String temp = text1;
31 text1 = text2;
32 text2 = temp;
33 }
34
35 int m = text1.length();
36 int n = text2.length();
37
38 // Only need current and previous row
39 int[] prev = new int[n + 1];
40 int[] curr = new int[n + 1];
41
42 for (int i = 1; i <= m; i++) {
43 for (int j = 1; j <= n; j++) {
44 if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
45 curr[j] = prev[j - 1] + 1;
46 } else {
47 curr[j] = Math.max(prev[j], curr[j - 1]);
48 }
49 }
50 // Swap arrays
51 int[] temp = prev;
52 prev = curr;
53 curr = temp;
54 }
55
56 return prev[n];
57 }
58
59 // Solution 3: Recursive with Memoization (Top-down)
60 public int longestCommonSubsequenceMemo(String text1, String text2) {
61 int m = text1.length();
62 int n = text2.length();
63 Integer[][] memo = new Integer[m][n];
64 return dp(text1, text2, 0, 0, memo);
65 }
66
67 private int dp(String text1, String text2, int i, int j, Integer[][] memo) {
68 // Base case: reached end of either string
69 if (i == text1.length() || j == text2.length()) {
70 return 0;
71 }
72
73 // Check memo
74 if (memo[i][j] != null) {
75 return memo[i][j];
76 }
77
78 // Match: extend LCS
79 if (text1.charAt(i) == text2.charAt(j)) {
80 memo[i][j] = 1 + dp(text1, text2, i + 1, j + 1, memo);
81 } else {
82 // No match: try both options
83 memo[i][j] = Math.max(
84 dp(text1, text2, i + 1, j, memo),
85 dp(text1, text2, i, j + 1, memo)
86 );
87 }
88
89 return memo[i][j];
90 }
91
92 // Bonus: Reconstruct the actual LCS string
93 public String getLCS(String text1, String text2) {
94 int m = text1.length();
95 int n = text2.length();
96 int[][] dp = new int[m + 1][n + 1];
97
98 // Build DP table
99 for (int i = 1; i <= m; i++) {
100 for (int j = 1; j <= n; j++) {
101 if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
102 dp[i][j] = dp[i - 1][j - 1] + 1;
103 } else {
104 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
105 }
106 }
107 }
108
109 // Backtrack to build LCS
110 StringBuilder lcs = new StringBuilder();
111 int i = m, j = n;
112
113 while (i > 0 && j > 0) {
114 if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
115 lcs.append(text1.charAt(i - 1));
116 i--;
117 j--;
118 } else if (dp[i - 1][j] > dp[i][j - 1]) {
119 i--;
120 } else {
121 j--;
122 }
123 }
124
125 return lcs.reverse().toString();
126 }
127}
128