From df30da1e154afe6c769ed357cfdd794ceebce408 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Wed, 2 Oct 2024 19:47:08 +0530 Subject: [PATCH 1/7] Enhance comments & improve readability in LongestCommonSubsequence.java --- .../LongestCommonSubsequence.java | 66 ++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java index 2d1fa1d1153f..13723e143bc2 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java @@ -1,72 +1,118 @@ package com.thealgorithms.dynamicprogramming; +/** + * This class implements the Longest Common Subsequence (LCS) problem. + * The LCS of two sequences is the longest sequence that appears in both + * sequences + * in the same order, but not necessarily consecutively. + * + * This implementation uses dynamic programming to find the LCS of two strings. + */ final class LongestCommonSubsequence { + private LongestCommonSubsequence() { } + /** + * Returns the Longest Common Subsequence (LCS) of two given strings. + * + * @param str1 The first string. + * @param str2 The second string. + * @return The LCS of the two strings, or null if one of the strings is null. + */ public static String getLCS(String str1, String str2) { - // At least one string is null + // If either string is null, return null as LCS can't be computed. if (str1 == null || str2 == null) { return null; } - - // At least one string is empty + // If either string is empty, return an empty string as LCS. if (str1.length() == 0 || str2.length() == 0) { return ""; } + // Convert the strings into arrays of characters String[] arr1 = str1.split(""); String[] arr2 = str2.split(""); - // lcsMatrix[i][j] = LCS of first i elements of arr1 and first j characters of arr2 + // lcsMatrix[i][j] = LCS(first i characters of str1, first j characters of str2) int[][] lcsMatrix = new int[arr1.length + 1][arr2.length + 1]; + // Base Case: Fill the LCS matrix 0th row & 0th column with 0s + // as LCS of any string with an empty string is 0. for (int i = 0; i < arr1.length + 1; i++) { lcsMatrix[i][0] = 0; } for (int j = 1; j < arr2.length + 1; j++) { lcsMatrix[0][j] = 0; } + + // Build the LCS matrix by comparing characters of str1 & str2 for (int i = 1; i < arr1.length + 1; i++) { for (int j = 1; j < arr2.length + 1; j++) { + // If characters match, the LCS increases by 1 if (arr1[i - 1].equals(arr2[j - 1])) { lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1; } else { + // Otherwise, take the maximum of the left or above values lcsMatrix[i][j] = Math.max(lcsMatrix[i - 1][j], lcsMatrix[i][j - 1]); } } } + + // Call helper function to reconstruct the LCS from the matrix return lcsString(str1, str2, lcsMatrix); } + /** + * Reconstructs the LCS string from the LCS matrix. + * + * @param str1 The first string. + * @param str2 The second string. + * @param lcsMatrix The matrix storing the lengths of LCSs of substrings of str1 + * and str2. + * @return The LCS string. + */ public static String lcsString(String str1, String str2, int[][] lcsMatrix) { - StringBuilder lcs = new StringBuilder(); - int i = str1.length(); - int j = str2.length(); + StringBuilder lcs = new StringBuilder(); // Hold the LCS characters. + int i = str1.length(); // Start from the end of str1. + int j = str2.length(); // Start from the end of str2. + + // Trace back through the LCS matrix to reconstruct the LCS while (i > 0 && j > 0) { + // If characters match, add to the LCS and move diagonally in the matrix if (str1.charAt(i - 1) == str2.charAt(j - 1)) { lcs.append(str1.charAt(i - 1)); i--; j--; } else if (lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1]) { + // If the value above is larger, move up i--; } else { + // If the value to the left is larger, move left j--; } } - return lcs.reverse().toString(); + + return lcs.reverse().toString(); // LCS built in reverse, so reverse it back } + /** + * Main method to run a sample test for the LCS implementation. + * + * @param args Command-line arguments (not used in this case). + */ public static void main(String[] args) { String str1 = "DSGSHSRGSRHTRD"; String str2 = "DATRGAGTSHS"; + + // Get the LCS of str1 and str2 String lcs = getLCS(str1, str2); - // Print LCS + // Print the results if (lcs != null) { System.out.println("String 1: " + str1); System.out.println("String 2: " + str2); - System.out.println("LCS: " + lcs); + System.out.println("Longest Common Subsequence (LCS): " + lcs); System.out.println("LCS length: " + lcs.length()); } } From ad63b6e7d6a358452baa36fd012cf0036e67096d Mon Sep 17 00:00:00 2001 From: Hardvan Date: Fri, 4 Oct 2024 14:19:25 +0000 Subject: [PATCH 2/7] Update directory --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 24cd22f67d07..af5905d00747 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -238,6 +238,7 @@ * [KnapsackMemoization](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java) * [LevenshteinDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java) * [LongestAlternatingSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java) + * [LongestArithmeticSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java) * [LongestCommonSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java) * [LongestIncreasingSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequence.java) * [LongestPalindromicSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubsequence.java) @@ -733,6 +734,7 @@ * [KnapsackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java) * [LevenshteinDistanceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java) * [LongestAlternatingSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java) + * [LongestArithmeticSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java) * [LongestIncreasingSubsequenceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java) * [LongestPalindromicSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java) * [LongestValidParenthesesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java) From 22c2100299b92d8c28e56db720b41a9a302ea8d8 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Fri, 4 Oct 2024 19:50:46 +0530 Subject: [PATCH 3/7] Remove main method --- .../LongestCommonSubsequence.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java index 13723e143bc2..d7206ca94dff 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java @@ -95,25 +95,4 @@ public static String lcsString(String str1, String str2, int[][] lcsMatrix) { return lcs.reverse().toString(); // LCS built in reverse, so reverse it back } - - /** - * Main method to run a sample test for the LCS implementation. - * - * @param args Command-line arguments (not used in this case). - */ - public static void main(String[] args) { - String str1 = "DSGSHSRGSRHTRD"; - String str2 = "DATRGAGTSHS"; - - // Get the LCS of str1 and str2 - String lcs = getLCS(str1, str2); - - // Print the results - if (lcs != null) { - System.out.println("String 1: " + str1); - System.out.println("String 2: " + str2); - System.out.println("Longest Common Subsequence (LCS): " + lcs); - System.out.println("LCS length: " + lcs.length()); - } - } } From f7fd7f1187d62c037ab66dd75506e93bc0acb92f Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Fri, 4 Oct 2024 19:52:01 +0530 Subject: [PATCH 4/7] Add tests --- .../LongestCommonSubsequenceTest.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java new file mode 100644 index 000000000000..9dfdb760f858 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java @@ -0,0 +1,89 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LongestCommonSubsequenceTest { + + @Test + public void testLCSBasic() { + String str1 = "ABCBDAB"; + String str2 = "BDCAB"; + String expected = "BDAB"; // The longest common subsequence + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSIdenticalStrings() { + String str1 = "AGGTAB"; + String str2 = "AGGTAB"; + String expected = "AGGTAB"; // LCS is the same as the strings + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSNoCommonCharacters() { + String str1 = "ABC"; + String str2 = "XYZ"; + String expected = ""; // No common subsequence + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithEmptyString() { + String str1 = ""; + String str2 = "XYZ"; + String expected = ""; // LCS with an empty string should be empty + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithBothEmptyStrings() { + String str1 = ""; + String str2 = ""; + String expected = ""; // LCS with both strings empty should be empty + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullFirstString() { + String str1 = null; + String str2 = "XYZ"; + String expected = null; // Should return null if first string is null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullSecondString() { + String str1 = "ABC"; + String str2 = null; + String expected = null; // Should return null if second string is null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullBothStrings() { + String str1 = null; + String str2 = null; + String expected = null; // Should return null if both strings are null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithLongerStringContainingCommonSubsequence() { + String str1 = "ABCDEF"; + String str2 = "AEBDF"; + String expected = "ABD"; // Common subsequence is "ABD" + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } +} From 9c1ff5a120a9038fc52786503d736132b0ad8082 Mon Sep 17 00:00:00 2001 From: Hardvan Date: Fri, 4 Oct 2024 14:22:23 +0000 Subject: [PATCH 5/7] Update directory --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index af5905d00747..c1f9fac86ffd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -735,6 +735,7 @@ * [LevenshteinDistanceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java) * [LongestAlternatingSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java) * [LongestArithmeticSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java) + * [LongestCommonSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java) * [LongestIncreasingSubsequenceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java) * [LongestPalindromicSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java) * [LongestValidParenthesesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java) From 51833e04d7919f6e2e71a97d6ab744d222136a99 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Fri, 4 Oct 2024 19:53:43 +0530 Subject: [PATCH 6/7] Fix comment --- .../dynamicprogramming/LongestCommonSubsequence.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java index d7206ca94dff..54837b5f4e71 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java @@ -68,8 +68,8 @@ public static String getLCS(String str1, String str2) { * * @param str1 The first string. * @param str2 The second string. - * @param lcsMatrix The matrix storing the lengths of LCSs of substrings of str1 - * and str2. + * @param lcsMatrix The matrix storing the lengths of LCSs + * of substrings of str1 and str2. * @return The LCS string. */ public static String lcsString(String str1, String str2, int[][] lcsMatrix) { From aa5c5f18e958b679b0801555ba73c5abf23b6f47 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Fri, 4 Oct 2024 19:55:24 +0530 Subject: [PATCH 7/7] Fix test --- .../dynamicprogramming/LongestCommonSubsequenceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java index 9dfdb760f858..40bbdff15ca6 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java @@ -82,7 +82,7 @@ public void testLCSWithNullBothStrings() { public void testLCSWithLongerStringContainingCommonSubsequence() { String str1 = "ABCDEF"; String str2 = "AEBDF"; - String expected = "ABD"; // Common subsequence is "ABD" + String expected = "ABDF"; // Common subsequence is "ABDF" String result = LongestCommonSubsequence.getLCS(str1, str2); assertEquals(expected, result); }