Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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"
*
* @see <a href="https://en.wikipedia.org/wiki/Longest_common_substring">
* Wikipedia: Longest Common Substring</a>
*
* author: Vraj Prajapati @Rosander0
*/
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 exists.
* If multiple substrings share the maximum length, the first one found is returned.
*/
public static String longestCommonSubstring(final String a, final String b) {
if (a == null || b == null || a.isEmpty() || b.isEmpty()) {
return "";
Comment thread
Rosander0 marked this conversation as resolved.
}

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;
}
} else {
dp[i][j] = 0;
}
}
}

if (maxLength == 0) {
return "";
}
return a.substring(endIndex - maxLength, endIndex);
Comment thread
Rosander0 marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.thealgorithms.strings;
// author: Vraj Prajapati @Rosander0

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class LongestCommonSubstringTest {

@Test
public void testNullOrEmptyInputs() {
assertEquals("", LongestCommonSubstring.longestCommonSubstring(null, "abc"));
assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", null));
assertEquals("", LongestCommonSubstring.longestCommonSubstring("", "abc"));
assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", ""));
}

@Test
public void testNormalSubstrings() {
assertEquals("cde", LongestCommonSubstring.longestCommonSubstring("abcdef", "zcdemf"));
assertEquals("abc", LongestCommonSubstring.longestCommonSubstring("abc", "abc"));
assertEquals("cdef", LongestCommonSubstring.longestCommonSubstring("abcdef", "cdefgh"));
}

@Test
public void testSingleCharacterAndNoMatch() {
assertEquals("a", LongestCommonSubstring.longestCommonSubstring("a", "a"));
assertEquals("", LongestCommonSubstring.longestCommonSubstring("abc", "xyz"));
}

@Test
public void testMultipleMatchesFirstLongest() {
// Keeps the first matched longest substring when lengths are tied
assertEquals("abc", LongestCommonSubstring.longestCommonSubstring("abcXdef", "abcYdef"));
Comment thread
Rosander0 marked this conversation as resolved.
}
}
Loading