From bb26138b5eb79f5e257d2fc9d7ad40d98f682723 Mon Sep 17 00:00:00 2001 From: Saurabh Kokate Date: Sat, 4 Oct 2025 20:35:19 +0530 Subject: [PATCH 1/4] Add Regular Expression Matching algorithm using DP with test cases --- .../RegularExpressionMatching.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java new file mode 100644 index 000000000000..1f04958856e3 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java @@ -0,0 +1,76 @@ +package com.thealgorithms.dynamicprogramming; + +public class RegularExpressionMatching { + + /** + * Function to implement regex matching with '.' and '*'. + * '.' Matches any single character + * '*' Matches zero or more of the preceding element + * + * @param s input string + * @param p pattern string + * @return true if the string matches the pattern, false otherwise + */ + public boolean isMatch(String s, String p) { + Boolean[][] memo = new Boolean[s.length() + 1][p.length() + 1]; + return dp(0, 0, s, p, memo); + } + + private boolean dp(int i, int j, String s, String p, Boolean[][] memo) { + if (memo[i][j] != null) return memo[i][j]; + + boolean ans; + if (j == p.length()) { + // If pattern is finished, string must also be finished + ans = (i == s.length()); + } else { + // Check if first character matches + boolean firstMatch = (i < s.length() && + (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')); + + // Handle '*' in the pattern + if (j + 1 < p.length() && p.charAt(j + 1) == '*') { + // Two options: + // 1. Skip the "x*" in the pattern + // 2. If firstMatch is true, consume one char from string + ans = dp(i, j + 2, s, p, memo) || (firstMatch && dp(i + 1, j, s, p, memo)); + } else { + ans = firstMatch && dp(i + 1, j + 1, s, p, memo); + } + } + + memo[i][j] = ans; + return ans; + } + + // ----------------------- + // Main function with test cases + // ----------------------- + public static void main(String[] args) { + RegularExpressionMatching solution = new RegularExpressionMatching(); + + String[][] testCases = { + {"aa", "a", "false"}, + {"aa", "a*", "true"}, + {"ab", ".*", "true"}, + {"mississippi", "mis*is*p*.", "false"}, + {"ab", ".*c", "false"}, + {"aaa", "a*a", "true"}, + {"aab", "c*a*b", "true"}, + {"", ".*", "true"}, + {"", "", "true"}, + {"abcd", "d*", "false"} + }; + + System.out.println("Testing Regular Expression Matching (DP with Memoization)\n"); + + for (String[] test : testCases) { + String s = test[0]; + String p = test[1]; + boolean expected = Boolean.parseBoolean(test[2]); + boolean result = solution.isMatch(s, p); + System.out.printf("Input: s = \"%s\", p = \"%s\" | Output: %b | Expected: %b | %s\n", + s, p, result, expected, (result == expected ? "✅" : "❌")); + } + } +} From 03697db97ae05ee0e9b1fd536f2b1078332b4c32 Mon Sep 17 00:00:00 2001 From: Banula Kumarage <63106638+BanulaKumarage@users.noreply.github.com> Date: Sat, 4 Oct 2025 19:26:08 +0530 Subject: [PATCH 2/4] Dev: Added algorithm to find the nth number in the Sylvester Sequence (#6613) Added algorithm to find the nth number in the SylvesterSequence --- .../recursion/SylvesterSequence.java | 50 ++++++++++++++++++ .../recursion/SylvesterSequenceTest.java | 51 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/main/java/com/thealgorithms/recursion/SylvesterSequence.java create mode 100644 src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java diff --git a/src/main/java/com/thealgorithms/recursion/SylvesterSequence.java b/src/main/java/com/thealgorithms/recursion/SylvesterSequence.java new file mode 100644 index 000000000000..2b39f66c7936 --- /dev/null +++ b/src/main/java/com/thealgorithms/recursion/SylvesterSequence.java @@ -0,0 +1,50 @@ +package com.thealgorithms.recursion; + +import java.math.BigInteger; + +/** + * A utility class for calculating numbers in Sylvester's sequence. + * + *

Sylvester's sequence is a sequence of integers where each term is calculated + * using the formula: + *

+ * a(n) = a(n-1) * (a(n-1) - 1) + 1
+ * 
+ * with the first term being 2. + * + *

This class is final and cannot be instantiated. + * + * @see Wikipedia: Sylvester sequence + */ +public final class SylvesterSequence { + + // Private constructor to prevent instantiation + private SylvesterSequence() { + } + + /** + * Calculates the nth number in Sylvester's sequence. + * + *

The sequence is defined recursively, with the first term being 2: + *

+     * a(1) = 2
+     * a(n) = a(n-1) * (a(n-1) - 1) + 1 for n > 1
+     * 
+ * + * @param n the position in the sequence (must be greater than 0) + * @return the nth number in Sylvester's sequence + * @throws IllegalArgumentException if n is less than or equal to 0 + */ + public static BigInteger sylvester(int n) { + if (n <= 0) { + throw new IllegalArgumentException("sylvester() does not accept negative numbers or zero."); + } + if (n == 1) { + return BigInteger.valueOf(2); + } else { + BigInteger prev = sylvester(n - 1); + // Sylvester sequence formula: a(n) = a(n-1) * (a(n-1) - 1) + 1 + return prev.multiply(prev.subtract(BigInteger.ONE)).add(BigInteger.ONE); + } + } +} diff --git a/src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java b/src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java new file mode 100644 index 000000000000..3ea5641ac484 --- /dev/null +++ b/src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.recursion; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +class SylvesterSequenceTest { + + /** + * Provides test cases for valid Sylvester sequence numbers. + * Format: { n, expectedValue } + */ + static Stream validSylvesterNumbers() { + return Stream.of(new Object[] {1, BigInteger.valueOf(2)}, new Object[] {2, BigInteger.valueOf(3)}, new Object[] {3, BigInteger.valueOf(7)}, new Object[] {4, BigInteger.valueOf(43)}, new Object[] {5, BigInteger.valueOf(1807)}, new Object[] {6, new BigInteger("3263443")}, + new Object[] {7, new BigInteger("10650056950807")}, new Object[] {8, new BigInteger("113423713055421844361000443")}); + } + + @ParameterizedTest + @MethodSource("validSylvesterNumbers") + void testSylvesterValidNumbers(int n, BigInteger expected) { + assertEquals(expected, SylvesterSequence.sylvester(n), "Sylvester sequence value mismatch for n=" + n); + } + + /** + * Test edge case for n <= 0 which should throw IllegalArgumentException + */ + @ParameterizedTest + @ValueSource(ints = {0, -1, -10, -100}) + void testSylvesterInvalidZero(int n) { + assertThrows(IllegalArgumentException.class, () -> SylvesterSequence.sylvester(n)); + } + + /** + * Test a larger number to ensure no overflow occurs. + */ + @Test + void testSylvesterLargeNumber() { + int n = 10; + BigInteger result = SylvesterSequence.sylvester(n); + assertNotNull(result); + assertTrue(result.compareTo(BigInteger.ZERO) > 0, "Result should be positive"); + } +} From aa11e001082b65e3f6f7127ab6c6bb3b5cda011e Mon Sep 17 00:00:00 2001 From: Saurabh Kokate Date: Sat, 4 Oct 2025 20:58:51 +0530 Subject: [PATCH 3/4] feat: add Regular Expression Matching algorithm using DP --- .../RegularExpressionMatching.java | 66 +++++-------------- .../RegularExpressionMatchingTest.java | 28 ++++++++ 2 files changed, 45 insertions(+), 49 deletions(-) create mode 100644 src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java index 1f04958856e3..ee500b78dcf1 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java @@ -1,38 +1,37 @@ package com.thealgorithms.dynamicprogramming; +/** + * Class for Regular Expression Matching using Dynamic Programming. + */ public class RegularExpressionMatching { /** - * Function to implement regex matching with '.' and '*'. - * '.' Matches any single character - * '*' Matches zero or more of the preceding element + * Determines if the input string matches the given pattern. * - * @param s input string - * @param p pattern string + * @param s the input string + * @param p the pattern string (may contain '.' and '*') * @return true if the string matches the pattern, false otherwise */ - public boolean isMatch(String s, String p) { - Boolean[][] memo = new Boolean[s.length() + 1][p.length() + 1]; + public boolean isMatch(final String s, final String p) { + final Boolean[][] memo = new Boolean[s.length() + 1][p.length() + 1]; return dp(0, 0, s, p, memo); } - private boolean dp(int i, int j, String s, String p, Boolean[][] memo) { - if (memo[i][j] != null) return memo[i][j]; + private boolean dp(final int i, final int j, final String s, final String p, final Boolean[][] memo) { + if (memo[i][j] != null) { + return memo[i][j]; + } - boolean ans; + final boolean ans; if (j == p.length()) { - // If pattern is finished, string must also be finished ans = (i == s.length()); } else { - // Check if first character matches - boolean firstMatch = (i < s.length() && - (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')); + final boolean firstMatch = (i < s.length() && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')); - // Handle '*' in the pattern if (j + 1 < p.length() && p.charAt(j + 1) == '*') { // Two options: - // 1. Skip the "x*" in the pattern - // 2. If firstMatch is true, consume one char from string + // 1. Skip the pattern with '*' + // 2. Use '*' to match one character ans = dp(i, j + 2, s, p, memo) || (firstMatch && dp(i + 1, j, s, p, memo)); } else { ans = firstMatch && dp(i + 1, j + 1, s, p, memo); @@ -42,35 +41,4 @@ private boolean dp(int i, int j, String s, String p, Boolean[][] memo) { memo[i][j] = ans; return ans; } - - // ----------------------- - // Main function with test cases - // ----------------------- - public static void main(String[] args) { - RegularExpressionMatching solution = new RegularExpressionMatching(); - - String[][] testCases = { - {"aa", "a", "false"}, - {"aa", "a*", "true"}, - {"ab", ".*", "true"}, - {"mississippi", "mis*is*p*.", "false"}, - {"ab", ".*c", "false"}, - {"aaa", "a*a", "true"}, - {"aab", "c*a*b", "true"}, - {"", ".*", "true"}, - {"", "", "true"}, - {"abcd", "d*", "false"} - }; - - System.out.println("Testing Regular Expression Matching (DP with Memoization)\n"); - - for (String[] test : testCases) { - String s = test[0]; - String p = test[1]; - boolean expected = Boolean.parseBoolean(test[2]); - boolean result = solution.isMatch(s, p); - System.out.printf("Input: s = \"%s\", p = \"%s\" | Output: %b | Expected: %b | %s\n", - s, p, result, expected, (result == expected ? "✅" : "❌")); - } - } -} +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java new file mode 100644 index 000000000000..afc0262b63ff --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + +/** + * Test class for RegularExpressionMatching. + */ +public class RegularExpressionMatchingTest { + + @Test + public void testBasicCases() { + final RegularExpressionMatching matcher = new RegularExpressionMatching(); + + // Test cases from the problem + assertFalse(matcher.isMatch("aa", "a")); + assertTrue(matcher.isMatch("aa", "a*")); + assertTrue(matcher.isMatch("ab", ".*")); + assertFalse(matcher.isMatch("mississippi", "mis*is*p*.")); + assertFalse(matcher.isMatch("ab", ".*c")); + assertTrue(matcher.isMatch("aaa", "a*a")); + assertTrue(matcher.isMatch("aab", "c*a*b")); + assertTrue(matcher.isMatch("", ".*")); + assertTrue(matcher.isMatch("", "")); + assertFalse(matcher.isMatch("abcd", "d*")); + } +} \ No newline at end of file From f565a948d671cebf8098435c3f7973938c19065c Mon Sep 17 00:00:00 2001 From: Saurabh Kokate Date: Sat, 4 Oct 2025 21:08:59 +0530 Subject: [PATCH 4/4] fix: resolve checkstyle issues - add newlines and remove trailing spaces --- .../dynamicprogramming/RegularExpressionMatching.java | 2 +- .../dynamicprogramming/RegularExpressionMatchingTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java index ee500b78dcf1..6a15f4528d06 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatching.java @@ -41,4 +41,4 @@ private boolean dp(final int i, final int j, final String s, final String p, fin memo[i][j] = ans; return ans; } -} \ No newline at end of file +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java index afc0262b63ff..a29a7b57419a 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/RegularExpressionMatchingTest.java @@ -12,7 +12,7 @@ public class RegularExpressionMatchingTest { @Test public void testBasicCases() { final RegularExpressionMatching matcher = new RegularExpressionMatching(); - + // Test cases from the problem assertFalse(matcher.isMatch("aa", "a")); assertTrue(matcher.isMatch("aa", "a*")); @@ -25,4 +25,4 @@ public void testBasicCases() { assertTrue(matcher.isMatch("", "")); assertFalse(matcher.isMatch("abcd", "d*")); } -} \ No newline at end of file +}