|
1 | 1 | package com.thealgorithms.dynamicprogramming;
|
2 | 2 |
|
| 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 | + */ |
3 | 11 | final class LongestCommonSubsequence {
|
| 12 | + |
4 | 13 | private LongestCommonSubsequence() {
|
5 | 14 | }
|
6 | 15 |
|
| 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 | + */ |
7 | 23 | 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. |
9 | 25 | if (str1 == null || str2 == null) {
|
10 | 26 | return null;
|
11 | 27 | }
|
12 |
| - |
13 |
| - // At least one string is empty |
| 28 | + // If either string is empty, return an empty string as LCS. |
14 | 29 | if (str1.length() == 0 || str2.length() == 0) {
|
15 | 30 | return "";
|
16 | 31 | }
|
17 | 32 |
|
| 33 | + // Convert the strings into arrays of characters |
18 | 34 | String[] arr1 = str1.split("");
|
19 | 35 | String[] arr2 = str2.split("");
|
20 | 36 |
|
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) |
22 | 38 | int[][] lcsMatrix = new int[arr1.length + 1][arr2.length + 1];
|
23 | 39 |
|
| 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. |
24 | 42 | for (int i = 0; i < arr1.length + 1; i++) {
|
25 | 43 | lcsMatrix[i][0] = 0;
|
26 | 44 | }
|
27 | 45 | for (int j = 1; j < arr2.length + 1; j++) {
|
28 | 46 | lcsMatrix[0][j] = 0;
|
29 | 47 | }
|
| 48 | + |
| 49 | + // Build the LCS matrix by comparing characters of str1 & str2 |
30 | 50 | for (int i = 1; i < arr1.length + 1; i++) {
|
31 | 51 | for (int j = 1; j < arr2.length + 1; j++) {
|
| 52 | + // If characters match, the LCS increases by 1 |
32 | 53 | if (arr1[i - 1].equals(arr2[j - 1])) {
|
33 | 54 | lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1;
|
34 | 55 | } else {
|
| 56 | + // Otherwise, take the maximum of the left or above values |
35 | 57 | lcsMatrix[i][j] = Math.max(lcsMatrix[i - 1][j], lcsMatrix[i][j - 1]);
|
36 | 58 | }
|
37 | 59 | }
|
38 | 60 | }
|
| 61 | + |
| 62 | + // Call helper function to reconstruct the LCS from the matrix |
39 | 63 | return lcsString(str1, str2, lcsMatrix);
|
40 | 64 | }
|
41 | 65 |
|
| 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 | + */ |
42 | 75 | 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 |
46 | 81 | while (i > 0 && j > 0) {
|
| 82 | + // If characters match, add to the LCS and move diagonally in the matrix |
47 | 83 | if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
|
48 | 84 | lcs.append(str1.charAt(i - 1));
|
49 | 85 | i--;
|
50 | 86 | j--;
|
51 | 87 | } else if (lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1]) {
|
| 88 | + // If the value above is larger, move up |
52 | 89 | i--;
|
53 | 90 | } else {
|
| 91 | + // If the value to the left is larger, move left |
54 | 92 | j--;
|
55 | 93 | }
|
56 | 94 | }
|
57 |
| - return lcs.reverse().toString(); |
58 |
| - } |
59 | 95 |
|
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 |
72 | 97 | }
|
73 | 98 | }
|
0 commit comments