From 8d74a321971f74ba212854592e53a3afd0a57cc4 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Wed, 10 Jun 2026 18:30:04 +0530 Subject: [PATCH 1/9] Add LongestCommonSubstring Impleamentation --- .../strings/LongestCommonSubstring.java | 41 +++++++++++++++++++ .../strings/LongestCommonSubstringTest.java | 33 +++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java create mode 100644 src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java new file mode 100644 index 000000000000..e304554c6d8e --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java @@ -0,0 +1,41 @@ +package com.thealgorithms.strings; + +/** + * Longest Common Substring finds the longest string that is a + * contiguous substring of two input strings. + * Example: "abcdef" and "zcdemf" → "cde" + */ +public final class LongestCommonSubstring { + + private LongestCommonSubstring() { + // Utility class + } + + /** + * Finds the longest common substring of two strings. + * + * @param a First input string + * @param b Second input string + * @return The longest common substring, or empty string if none found + */ + public static String longestCommonSubstring(final String a, final String b) { + if (a == null || b == null || a.isEmpty() || b.isEmpty()) { + return ""; + } + int[][] dp = new int[a.length() + 1][b.length() + 1]; + int maxLength = 0; + int endIndex = 0; + for (int i = 1; i <= a.length(); i++) { + for (int j = 1; j <= b.length(); j++) { + if (a.charAt(i - 1) == b.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + if (dp[i][j] > maxLength) { + maxLength = dp[i][j]; + endIndex = i; + } + } + } + } + return a.substring(endIndex - maxLength, endIndex); + } +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java new file mode 100644 index 000000000000..b61a733ce77e --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.strings; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestCommonSubstringTest { + + private record TestData(String a, String b, String expected) { + } + + private static Stream provideTestCases() { + return Stream.of( + new TestData(null, "abc", ""), + new TestData("abc", null, ""), + new TestData("", "abc", ""), + new TestData("abc", "", ""), + new TestData("abcdef", "zcdemf", "cde"), + new TestData("abc", "abc", "abc"), + new TestData("abc", "xyz", ""), + new TestData("abcdef", "cdefgh", "cdef"), + new TestData("a", "a", "a") + ); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testLongestCommonSubstring(TestData testData) { + Assertions.assertEquals(testData.expected(), + LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b())); + } +} \ No newline at end of file From ba9f7a2ce50a977738381fd43e02a01349d7e868 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Wed, 10 Jun 2026 19:41:44 +0530 Subject: [PATCH 2/9] fix: resolve build errors and formatting --- .../strings/LongestCommonSubstring.java | 8 ++++++-- .../strings/LongestCommonSubstringTest.java | 14 ++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java index e304554c6d8e..d20f68932015 100644 --- a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java @@ -1,9 +1,9 @@ package com.thealgorithms.strings; /** - * Longest Common Substring finds the longest string that is a + * Longest Common Substring finds the longest string that is a * contiguous substring of two input strings. - * Example: "abcdef" and "zcdemf" → "cde" + * Example: "abcdef" and "zcdemf" -> "cde" */ public final class LongestCommonSubstring { @@ -22,9 +22,11 @@ public static String longestCommonSubstring(final String a, final String b) { if (a == null || b == null || a.isEmpty() || b.isEmpty()) { return ""; } + int[][] dp = new int[a.length() + 1][b.length() + 1]; int maxLength = 0; int endIndex = 0; + for (int i = 1; i <= a.length(); i++) { for (int j = 1; j <= b.length(); j++) { if (a.charAt(i - 1) == b.charAt(j - 1)) { @@ -33,6 +35,8 @@ public static String longestCommonSubstring(final String a, final String b) { maxLength = dp[i][j]; endIndex = i; } + } else { + dp[i][j] = 0; } } } diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index b61a733ce77e..00a644b38ac8 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -1,16 +1,18 @@ package com.thealgorithms.strings; import java.util.stream.Stream; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -public class LongestCommonSubstringTest { +public final class LongestCommonSubstringTest { private record TestData(String a, String b, String expected) { } - private static Stream provideTestCases() { + // JUnit 5 matches this automatically because it has the same name as the test method + private static Stream testLongestCommonSubstring() { return Stream.of( new TestData(null, "abc", ""), new TestData("abc", null, ""), @@ -20,14 +22,14 @@ private static Stream provideTestCases() { new TestData("abc", "abc", "abc"), new TestData("abc", "xyz", ""), new TestData("abcdef", "cdefgh", "cdef"), - new TestData("a", "a", "a") + new TestData("a", "a", "a"), + new TestData("abcXdef", "abcYdef", "abc") ); } @ParameterizedTest - @MethodSource("provideTestCases") + @MethodSource // No string argument needed here anymore void testLongestCommonSubstring(TestData testData) { - Assertions.assertEquals(testData.expected(), - LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b())); + Assertions.assertEquals(testData.expected(), LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b())); } } \ No newline at end of file From 2398c55ad844a2f0909afc85e8cfde47bf9d69b3 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Wed, 10 Jun 2026 19:49:58 +0530 Subject: [PATCH 3/9] fix: resolve build errors and formatting --- .../com/thealgorithms/strings/LongestCommonSubstring.java | 4 ++-- .../thealgorithms/strings/LongestCommonSubstringTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java index d20f68932015..645a997cbd12 100644 --- a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java @@ -22,11 +22,11 @@ public static String longestCommonSubstring(final String a, final String b) { if (a == null || b == null || a.isEmpty() || b.isEmpty()) { return ""; } - + int[][] dp = new int[a.length() + 1][b.length() + 1]; int maxLength = 0; int endIndex = 0; - + for (int i = 1; i <= a.length(); i++) { for (int j = 1; j <= b.length(); j++) { if (a.charAt(i - 1) == b.charAt(j - 1)) { diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index 00a644b38ac8..de10796ab16a 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -11,7 +11,6 @@ public final class LongestCommonSubstringTest { private record TestData(String a, String b, String expected) { } - // JUnit 5 matches this automatically because it has the same name as the test method private static Stream testLongestCommonSubstring() { return Stream.of( new TestData(null, "abc", ""), @@ -28,8 +27,9 @@ private static Stream testLongestCommonSubstring() { } @ParameterizedTest - @MethodSource // No string argument needed here anymore + @MethodSource void testLongestCommonSubstring(TestData testData) { - Assertions.assertEquals(testData.expected(), LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b())); + String actual = LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b()); + Assertions.assertEquals(testData.expected(), actual); } } \ No newline at end of file From a2dd3b5797c42f29787dfc4eba881f11412b212a Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Wed, 10 Jun 2026 20:11:06 +0530 Subject: [PATCH 4/9] Fix style: Add final newlines to string files --- .../strings/LongestCommonSubstringTest.java | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index de10796ab16a..aedb45ddc020 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -1,35 +1,34 @@ package com.thealgorithms.strings; -import java.util.stream.Stream; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; +public class LongestCommonSubstringTest { -public final class LongestCommonSubstringTest { + @Test + public void testNullOrEmptyInputs() { + assertEquals("", LongestCommonSubstring.longestCommonSubstring(null, "abc")); + assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", null)); + assertEquals("", LongestCommonSubstring.longestCommonSubstring("", "abc")); + assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", "")); + } - private record TestData(String a, String b, String expected) { + @Test + public void testNormalSubstrings() { + assertEquals("cde", LongestCommonSubstring.longestCommonSubstring("abcdef", "zcdemf")); + assertEquals("abc", LongestCommonSubstring.longestCommonSubstring("abc", "abc")); + assertEquals("cdef", LongestCommonSubstring.longestCommonSubstring("abcdef", "cdefgh")); } - private static Stream testLongestCommonSubstring() { - return Stream.of( - new TestData(null, "abc", ""), - new TestData("abc", null, ""), - new TestData("", "abc", ""), - new TestData("abc", "", ""), - new TestData("abcdef", "zcdemf", "cde"), - new TestData("abc", "abc", "abc"), - new TestData("abc", "xyz", ""), - new TestData("abcdef", "cdefgh", "cdef"), - new TestData("a", "a", "a"), - new TestData("abcXdef", "abcYdef", "abc") - ); + @Test + public void testSingleCharacterAndNoMatch() { + assertEquals("a", LongestCommonSubstring.longestCommonSubstring("a", "a")); + assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", "xyz")); } - @ParameterizedTest - @MethodSource - void testLongestCommonSubstring(TestData testData) { - String actual = LongestCommonSubstring.longestCommonSubstring(testData.a(), testData.b()); - Assertions.assertEquals(testData.expected(), actual); + @Test + public void testMultipleMatchesFirstLongest() { + // Keeps the first matched longest substring when lengths are tied + assertEquals("abc", LongestCommonSubstring.longestCommonSubstring("abcXdef", "abcYdef")); } } \ No newline at end of file From 0b3ef79b42b1d99219b3fcced50e21ad1d876ac6 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Wed, 10 Jun 2026 20:27:43 +0530 Subject: [PATCH 5/9] format: adding author --- .../java/com/thealgorithms/strings/LongestCommonSubstring.java | 2 ++ .../com/thealgorithms/strings/LongestCommonSubstringTest.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java index 645a997cbd12..1198ae74392f 100644 --- a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java @@ -4,6 +4,8 @@ * Longest Common Substring finds the longest string that is a * contiguous substring of two input strings. * Example: "abcdef" and "zcdemf" -> "cde" + * + * author: Vraj Prajapati @Rosander0 */ public final class LongestCommonSubstring { diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index aedb45ddc020..34d594e00a54 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -1,5 +1,5 @@ package com.thealgorithms.strings; - +//author: Vraj Prajapati @Rosander0 import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; From af645481f466222fb10fc00fe5b6c931e444b844 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Thu, 11 Jun 2026 10:34:42 +0530 Subject: [PATCH 6/9] Fix checkstyle: trailing spaces and missing newline at end of files --- .../com/thealgorithms/strings/LongestCommonSubstring.java | 4 ++-- .../com/thealgorithms/strings/LongestCommonSubstringTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java index 1198ae74392f..549a27aa32a9 100644 --- a/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestCommonSubstring.java @@ -4,7 +4,7 @@ * Longest Common Substring finds the longest string that is a * contiguous substring of two input strings. * Example: "abcdef" and "zcdemf" -> "cde" - * + * * author: Vraj Prajapati @Rosander0 */ public final class LongestCommonSubstring { @@ -44,4 +44,4 @@ public static String longestCommonSubstring(final String a, final String b) { } return a.substring(endIndex - maxLength, endIndex); } -} \ No newline at end of file +} diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index 34d594e00a54..7221bc251bb0 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -31,4 +31,4 @@ public void testMultipleMatchesFirstLongest() { // Keeps the first matched longest substring when lengths are tied assertEquals("abc", LongestCommonSubstring.longestCommonSubstring("abcXdef", "abcYdef")); } -} \ No newline at end of file +} From bad53d1635c9ed9e36a0dfa2a98beb4430c35dab Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Thu, 11 Jun 2026 10:49:58 +0530 Subject: [PATCH 7/9] fix: add spaces after comment slashes --- .../com/thealgorithms/strings/LongestCommonSubstringTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index 7221bc251bb0..519b309642d8 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -1,5 +1,5 @@ package com.thealgorithms.strings; -//author: Vraj Prajapati @Rosander0 +// author: Vraj Prajapati @Rosander0 import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; From e570206c2ac22193321be964cdc5b6dcbcbeb9bc Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Thu, 11 Jun 2026 10:57:27 +0530 Subject: [PATCH 8/9] fix: add blank line before imports --- .../com/thealgorithms/strings/LongestCommonSubstringTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index 519b309642d8..85aa04acfc92 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -1,5 +1,6 @@ package com.thealgorithms.strings; // author: Vraj Prajapati @Rosander0 + import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; From 64bfecb717db55ffe0c56936cca941900c7dfa44 Mon Sep 17 00:00:00 2001 From: Rosander0 Date: Thu, 11 Jun 2026 11:06:07 +0530 Subject: [PATCH 9/9] fix: add blank line before imports --- .../com/thealgorithms/strings/LongestCommonSubstringTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java index 85aa04acfc92..e54abcf2f1f3 100644 --- a/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java +++ b/src/test/java/com/thealgorithms/strings/LongestCommonSubstringTest.java @@ -2,6 +2,7 @@ // author: Vraj Prajapati @Rosander0 import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; public class LongestCommonSubstringTest {