Skip to content

Commit f34fe4d

Browse files
authored
Enhance comments & improve readability in LongestCommonSubsequence.java (#5523)
1 parent 3390273 commit f34fe4d

File tree

3 files changed

+136
-21
lines changed

3 files changed

+136
-21
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@
747747
* [LevenshteinDistanceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java)
748748
* [LongestAlternatingSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java)
749749
* [LongestArithmeticSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java)
750+
* [LongestCommonSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java)
750751
* [LongestIncreasingSubsequenceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java)
751752
* [LongestPalindromicSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java)
752753
* [LongestValidParenthesesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java)
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,98 @@
11
package com.thealgorithms.dynamicprogramming;
22

3+
/**
4+
* This class implements the Longest Common Subsequence (LCS) problem.
5+
* The LCS of two sequences is the longest sequence that appears in both
6+
* sequences
7+
* in the same order, but not necessarily consecutively.
8+
*
9+
* This implementation uses dynamic programming to find the LCS of two strings.
10+
*/
311
final class LongestCommonSubsequence {
12+
413
private LongestCommonSubsequence() {
514
}
615

16+
/**
17+
* Returns the Longest Common Subsequence (LCS) of two given strings.
18+
*
19+
* @param str1 The first string.
20+
* @param str2 The second string.
21+
* @return The LCS of the two strings, or null if one of the strings is null.
22+
*/
723
public static String getLCS(String str1, String str2) {
8-
// At least one string is null
24+
// If either string is null, return null as LCS can't be computed.
925
if (str1 == null || str2 == null) {
1026
return null;
1127
}
12-
13-
// At least one string is empty
28+
// If either string is empty, return an empty string as LCS.
1429
if (str1.length() == 0 || str2.length() == 0) {
1530
return "";
1631
}
1732

33+
// Convert the strings into arrays of characters
1834
String[] arr1 = str1.split("");
1935
String[] arr2 = str2.split("");
2036

21-
// lcsMatrix[i][j] = LCS of first i elements of arr1 and first j characters of arr2
37+
// lcsMatrix[i][j] = LCS(first i characters of str1, first j characters of str2)
2238
int[][] lcsMatrix = new int[arr1.length + 1][arr2.length + 1];
2339

40+
// Base Case: Fill the LCS matrix 0th row & 0th column with 0s
41+
// as LCS of any string with an empty string is 0.
2442
for (int i = 0; i < arr1.length + 1; i++) {
2543
lcsMatrix[i][0] = 0;
2644
}
2745
for (int j = 1; j < arr2.length + 1; j++) {
2846
lcsMatrix[0][j] = 0;
2947
}
48+
49+
// Build the LCS matrix by comparing characters of str1 & str2
3050
for (int i = 1; i < arr1.length + 1; i++) {
3151
for (int j = 1; j < arr2.length + 1; j++) {
52+
// If characters match, the LCS increases by 1
3253
if (arr1[i - 1].equals(arr2[j - 1])) {
3354
lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1;
3455
} else {
56+
// Otherwise, take the maximum of the left or above values
3557
lcsMatrix[i][j] = Math.max(lcsMatrix[i - 1][j], lcsMatrix[i][j - 1]);
3658
}
3759
}
3860
}
61+
62+
// Call helper function to reconstruct the LCS from the matrix
3963
return lcsString(str1, str2, lcsMatrix);
4064
}
4165

66+
/**
67+
* Reconstructs the LCS string from the LCS matrix.
68+
*
69+
* @param str1 The first string.
70+
* @param str2 The second string.
71+
* @param lcsMatrix The matrix storing the lengths of LCSs
72+
* of substrings of str1 and str2.
73+
* @return The LCS string.
74+
*/
4275
public static String lcsString(String str1, String str2, int[][] lcsMatrix) {
43-
StringBuilder lcs = new StringBuilder();
44-
int i = str1.length();
45-
int j = str2.length();
76+
StringBuilder lcs = new StringBuilder(); // Hold the LCS characters.
77+
int i = str1.length(); // Start from the end of str1.
78+
int j = str2.length(); // Start from the end of str2.
79+
80+
// Trace back through the LCS matrix to reconstruct the LCS
4681
while (i > 0 && j > 0) {
82+
// If characters match, add to the LCS and move diagonally in the matrix
4783
if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
4884
lcs.append(str1.charAt(i - 1));
4985
i--;
5086
j--;
5187
} else if (lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1]) {
88+
// If the value above is larger, move up
5289
i--;
5390
} else {
91+
// If the value to the left is larger, move left
5492
j--;
5593
}
5694
}
57-
return lcs.reverse().toString();
58-
}
5995

60-
public static void main(String[] args) {
61-
String str1 = "DSGSHSRGSRHTRD";
62-
String str2 = "DATRGAGTSHS";
63-
String lcs = getLCS(str1, str2);
64-
65-
// Print LCS
66-
if (lcs != null) {
67-
System.out.println("String 1: " + str1);
68-
System.out.println("String 2: " + str2);
69-
System.out.println("LCS: " + lcs);
70-
System.out.println("LCS length: " + lcs.length());
71-
}
96+
return lcs.reverse().toString(); // LCS built in reverse, so reverse it back
7297
}
7398
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class LongestCommonSubsequenceTest {
8+
9+
@Test
10+
public void testLCSBasic() {
11+
String str1 = "ABCBDAB";
12+
String str2 = "BDCAB";
13+
String expected = "BDAB"; // The longest common subsequence
14+
String result = LongestCommonSubsequence.getLCS(str1, str2);
15+
assertEquals(expected, result);
16+
}
17+
18+
@Test
19+
public void testLCSIdenticalStrings() {
20+
String str1 = "AGGTAB";
21+
String str2 = "AGGTAB";
22+
String expected = "AGGTAB"; // LCS is the same as the strings
23+
String result = LongestCommonSubsequence.getLCS(str1, str2);
24+
assertEquals(expected, result);
25+
}
26+
27+
@Test
28+
public void testLCSNoCommonCharacters() {
29+
String str1 = "ABC";
30+
String str2 = "XYZ";
31+
String expected = ""; // No common subsequence
32+
String result = LongestCommonSubsequence.getLCS(str1, str2);
33+
assertEquals(expected, result);
34+
}
35+
36+
@Test
37+
public void testLCSWithEmptyString() {
38+
String str1 = "";
39+
String str2 = "XYZ";
40+
String expected = ""; // LCS with an empty string should be empty
41+
String result = LongestCommonSubsequence.getLCS(str1, str2);
42+
assertEquals(expected, result);
43+
}
44+
45+
@Test
46+
public void testLCSWithBothEmptyStrings() {
47+
String str1 = "";
48+
String str2 = "";
49+
String expected = ""; // LCS with both strings empty should be empty
50+
String result = LongestCommonSubsequence.getLCS(str1, str2);
51+
assertEquals(expected, result);
52+
}
53+
54+
@Test
55+
public void testLCSWithNullFirstString() {
56+
String str1 = null;
57+
String str2 = "XYZ";
58+
String expected = null; // Should return null if first string is null
59+
String result = LongestCommonSubsequence.getLCS(str1, str2);
60+
assertEquals(expected, result);
61+
}
62+
63+
@Test
64+
public void testLCSWithNullSecondString() {
65+
String str1 = "ABC";
66+
String str2 = null;
67+
String expected = null; // Should return null if second string is null
68+
String result = LongestCommonSubsequence.getLCS(str1, str2);
69+
assertEquals(expected, result);
70+
}
71+
72+
@Test
73+
public void testLCSWithNullBothStrings() {
74+
String str1 = null;
75+
String str2 = null;
76+
String expected = null; // Should return null if both strings are null
77+
String result = LongestCommonSubsequence.getLCS(str1, str2);
78+
assertEquals(expected, result);
79+
}
80+
81+
@Test
82+
public void testLCSWithLongerStringContainingCommonSubsequence() {
83+
String str1 = "ABCDEF";
84+
String str2 = "AEBDF";
85+
String expected = "ABDF"; // Common subsequence is "ABDF"
86+
String result = LongestCommonSubsequence.getLCS(str1, str2);
87+
assertEquals(expected, result);
88+
}
89+
}

0 commit comments

Comments
 (0)