From abcbdc85803c4889ee5b282521b48518a402bb82 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Tue, 10 Feb 2026 23:57:42 +0500 Subject: [PATCH 01/26] Add IntelliJ IDEA run configurations to gitignore and JUnit launcher dependency --- .gitignore | 5 +++++ .idea/runConfigurations/Test.xml | 19 +++++++++++++++++++ pom.xml | 6 ++++++ 3 files changed, 30 insertions(+) create mode 100644 .idea/runConfigurations/Test.xml diff --git a/.gitignore b/.gitignore index a8000ba..451b456 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,8 @@ buildNumber.properties .DS_Store # End of https://www.toptal.com/developers/gitignore/api/jetbrains,java,intellij,maven + +### IntelliJ IDEA run configurations (override) ### +!.idea/ +.idea/* +!.idea/runConfigurations/ diff --git a/.idea/runConfigurations/Test.xml b/.idea/runConfigurations/Test.xml new file mode 100644 index 0000000..17e1f74 --- /dev/null +++ b/.idea/runConfigurations/Test.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 162ad5a..6968081 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,12 @@ 6.0.2 test + + org.junit.platform + junit-platform-launcher + 6.0.2 + test + org.openjdk.jmh jmh-core From 1fe47405814c97cf29122b058dd0a1f11f9a2452 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Feb 2026 00:26:56 +0500 Subject: [PATCH 02/26] Add solution progression to package-info and stub O(1) DP solution --- README.md | 9 +- .../NaiveSolution.java | 52 +++++++++ .../PrefixSuffixSolution.java | 52 +++++++++ .../Solution.java | 100 ++---------------- .../package-info.java | 19 +++- .../SolutionTest.java | 35 +++--- 6 files changed, 158 insertions(+), 109 deletions(-) create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java diff --git a/README.md b/README.md index 8cafacc..abbf950 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,19 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms. ``` src/main/java/codes/yam/leetcode/{problem-slug}/ - Solution.java # Solution implementation - package-info.java # Problem metadata + Solution.java # Optimal solution + NaiveSolution.java # Brute-force approach (optional) + PrefixSuffixSolution.java # Intermediate approach (optional) + package-info.java # Problem metadata & solution progression src/test/java/codes/yam/leetcode/{problem-slug}/ SolutionTest.java # Unit tests SolutionBenchmark.java # JMH benchmarks ``` +When a problem has multiple solution approaches, each gets its own class. `Solution.java` is always the optimal/final +version. The progression is documented in `package-info.java` and all solutions share the same test cases. + ## Commands ```bash diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java new file mode 100644 index 0000000..c769da1 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java @@ -0,0 +1,52 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Naive solution for the Minimum Deletions to Make String Balanced problem. + * + *

Tries every split point and counts misplaced characters at each. + * + *

    + *
  • Time Complexity: O(n²) + *
  • Space Complexity: O(1) + *
+ */ +class NaiveSolution { + /** + * Counts misplaced characters around the given split point in {@code s}. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @param index the split point index + * @return an array {@code [bCount, aCount]} where {@code bCount} is the number of {@code 'b'}s + * before {@code index} and {@code aCount} is the number of {@code 'a'}s after {@code index} + */ + int[] getSliceInfo(String s, int index) { + int aCount = 0, bCount = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == 'b' && i < index) { + bCount++; + } + if (c == 'a' && i > index) { + aCount++; + } + } + return new int[] {bCount, aCount}; + } + + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + int n = s.length(); + int min = n; + for (int i = 0; i < n; i++) { + int[] info = getSliceInfo(s, i); + int computed = info[0] + info[1]; + min = Math.min(min, computed); + } + return min; + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java new file mode 100644 index 0000000..2956294 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java @@ -0,0 +1,52 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Prefix/suffix solution for the Minimum Deletions to Make String Balanced problem. + * + *

Pre-computes prefix {@code 'b'} counts and suffix {@code 'a'} counts, then finds the optimal + * split point in a single pass. + * + *

    + *
  • Time Complexity: O(n) + *
  • Space Complexity: O(n) + *
+ */ +class PrefixSuffixSolution { + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + var aCountList = new int[s.length()]; + var bCountList = new int[s.length()]; + int aCount = 0; + int bCount = 0; + int n = s.length(); + int min = n; + + // count a's from right to left + for (int i = n - 1; i >= 0; i--) { + aCountList[i] = aCount; + if (s.charAt(i) == 'a') { + aCount++; + } + } + + // count b's from left to right + for (int i = 0; i < n; i++) { + bCountList[i] = bCount; + if (s.charAt(i) == 'b') { + bCount++; + } + } + + // calculate minimum + for (int i = 0; i < n; i++) { + min = Math.min(min, aCountList[i] + bCountList[i]); + } + + return min; + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java index cf70f49..09e874c 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java @@ -3,107 +3,25 @@ /** * Solution for the Minimum Deletions to Make String Balanced problem. * + *

Uses a single-pass DP approach, tracking the minimum deletions with constant space. + * *

    *
  • Time Complexity: O(n) - *
  • Space Complexity: O(n) + *
  • Space Complexity: O(1) *
+ * + * @see NaiveSolution + * @see PrefixSuffixSolution */ class Solution { /** - * Counts misplaced characters around the given split point in {@code s}. - * - *
    - *
  • Time Complexity: O(n) - *
  • Space Complexity: O(1) - *
- * - * @param s the string of {@code 'a'} and {@code 'b'} characters - * @param index the split point index - * @return an array {@code [bCount, aCount]} where {@code bCount} is the number of {@code 'b'}s - * before {@code index} and {@code aCount} is the number of {@code 'a'}s after {@code index} - */ - @SuppressWarnings("unused") - int[] getSliceInfo(String s, int index) { - int aCount = 0, bCount = 0; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == 'b' && i < index) { - bCount++; - } - if (c == 'a' && i > index) { - aCount++; - } - } - return new int[] {bCount, aCount}; - } - - /** - * Returns the minimum number of deletions to make {@code s} balanced, where balanced means no - * {@code 'b'} appears before an {@code 'a'}. Tries every split point and uses {@link - * #getSliceInfo(String, int)} to count misplaced characters at each. - * - *
    - *
  • Time Complexity: O(n²) - *
  • Space Complexity: O(1) - *
- * - * @param s the string of {@code 'a'} and {@code 'b'} characters - * @return the minimum number of deletions needed - */ - @SuppressWarnings("unused") - int minimumDeletionsNaive(String s) { - int n = s.length(); - int min = n; // we never need more than n deletions - for (int i = 0; i < n; i++) { - int[] info = getSliceInfo(s, i); - int computed = info[0] + info[1]; - min = Math.min(min, computed); - } - return min; - } - - /** - * Returns the minimum number of deletions to make {@code s} balanced, where balanced means no - * {@code 'b'} appears before an {@code 'a'}. Uses prefix/suffix counting to find the optimal - * split point in linear time. - * - *
    - *
  • Time Complexity: O(n) - *
  • Space Complexity: O(n) - *
+ * Returns the minimum number of deletions to make {@code s} balanced. * * @param s the string of {@code 'a'} and {@code 'b'} characters * @return the minimum number of deletions needed */ int minimumDeletions(String s) { - var aCountList = new int[s.length()]; - var bCountList = new int[s.length()]; - int aCount = 0; - int bCount = 0; - int n = s.length(); - int min = n; // we never need more than n deletions - - // count a's - for (int i = n - 1; i >= 0; i--) { - aCountList[i] = aCount; - if (s.charAt(i) == 'a') { - aCount++; - } - } - - // count b's - for (int i = 0; i < n; i++) { - bCountList[i] = bCount; - if (s.charAt(i) == 'b') { - bCount++; - } - } - - // calculate minimum - for (int i = 0; i < n; i++) { - min = Math.min(min, aCountList[i] + bCountList[i]); - } - - return min; + // TODO: Implement O(1) space DP solution + throw new UnsupportedOperationException("Not yet implemented"); } } diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index 29690e0..7b0d1a1 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -1,10 +1,21 @@ /** - *

Solutions for the "Minimum Deletions to Make String Balanced" problem on LeetCode.

+ * Solutions for the "Minimum Deletions to Make String Balanced" problem on LeetCode. + * *
    - *
  • Slug: minimum-deletions-to-make-string-balanced
  • - *
  • Difficulty: Medium
  • + *
  • Slug: minimum-deletions-to-make-string-balanced + *
  • Difficulty: Medium *
* - * @see Problem Link + *

Solution progression: + * + *

    + *
  1. {@link NaiveSolution} — Brute force, O(n²) time, O(1) space + *
  2. {@link PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) + * space + *
  3. {@link Solution} — Single-pass DP, O(n) time, O(1) space + *
+ * + * @see Problem + * Link */ package codes.yam.leetcode.minimumdeletionstomakestringbalanced; diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java index 2fa770d..eda2e9c 100644 --- a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java @@ -1,29 +1,40 @@ package codes.yam.leetcode.minimumdeletionstomakestringbalanced; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.params.provider.Arguments.arguments; - public class SolutionTest { static Stream cases() { return Stream.of( arguments("aababbab", 2), - arguments("bbaaaaabb", 2), - arguments("b", 0), - arguments("ba", 1), - arguments("aabbbaa", 2), - arguments("abababab", 3), - arguments("bbbbaaaa", 4)); + arguments("bbaaaaabb", 2), + arguments("b", 0), + arguments("ba", 1), + arguments("aabbbaa", 2), + arguments("abababab", 3), + arguments("bbbbaaaa", 4)); + } + + @ParameterizedTest + @MethodSource("cases") + void naiveSolution(String s, int expected) { + assertEquals(expected, new NaiveSolution().minimumDeletions(s)); + } + + @ParameterizedTest + @MethodSource("cases") + void prefixSuffixSolution(String s, int expected) { + assertEquals(expected, new PrefixSuffixSolution().minimumDeletions(s)); } @ParameterizedTest @MethodSource("cases") - void minimumDeletions(String s, int expected) { + void solution(String s, int expected) { assertEquals(expected, new Solution().minimumDeletions(s)); } } From 0ef02a27fa93b2922194981d826c7e83a270a0fd Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Feb 2026 00:33:29 +0500 Subject: [PATCH 03/26] Refactor solution links to use code tags for clarity --- .../minimumdeletionstomakestringbalanced/package-info.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index 7b0d1a1..995bf88 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -9,10 +9,10 @@ *

Solution progression: * *

    - *
  1. {@link NaiveSolution} — Brute force, O(n²) time, O(1) space - *
  2. {@link PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) + *
  3. {@code NaiveSolution} — Brute force, O(n²) time, O(1) space + *
  4. {@code PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) * space - *
  5. {@link Solution} — Single-pass DP, O(n) time, O(1) space + *
  6. {@code Solution} — Single-pass DP, O(n) time, O(1) space *
* * @see Problem From 87750a967c0c234638ba3a7ced988bfd1317145c Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Feb 2026 00:55:57 +0500 Subject: [PATCH 04/26] Update solution progression to reflect new implementations --- README.md | 9 ++-- .../DpSolution.java | 24 +++++++++ .../MemoizedSolution.java | 24 +++++++++ .../NaiveSolution.java | 52 ------------------- .../PrefixSuffixSolution.java | 52 ------------------- .../RecursiveSolution.java | 24 +++++++++ .../Solution.java | 7 +-- .../package-info.java | 8 +-- .../SolutionTest.java | 14 +++-- 9 files changed, 94 insertions(+), 120 deletions(-) create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java delete mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java delete mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java diff --git a/README.md b/README.md index abbf950..b0de941 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,11 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms. ``` src/main/java/codes/yam/leetcode/{problem-slug}/ - Solution.java # Optimal solution - NaiveSolution.java # Brute-force approach (optional) - PrefixSuffixSolution.java # Intermediate approach (optional) - package-info.java # Problem metadata & solution progression + Solution.java # Optimal solution + RecursiveSolution.java # Brute-force recursion (optional) + MemoizedSolution.java # Memoized recursion (optional) + DpSolution.java # Bottom-up DP (optional) + package-info.java # Problem metadata & solution progression src/test/java/codes/yam/leetcode/{problem-slug}/ SolutionTest.java # Unit tests diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java new file mode 100644 index 0000000..1c7f6ed --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java @@ -0,0 +1,24 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Bottom-up DP solution for the Minimum Deletions to Make String Balanced problem. + * + *

Flips the memoized recursion into an iterative DP table. + * + *

    + *
  • Time Complexity: O(n) + *
  • Space Complexity: O(n) + *
+ */ +class DpSolution { + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + // TODO: Implement bottom-up DP solution + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java new file mode 100644 index 0000000..e871e87 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java @@ -0,0 +1,24 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Memoized solution for the Minimum Deletions to Make String Balanced problem. + * + *

Same recursive approach as {@code RecursiveSolution}, but caches overlapping subproblems. + * + *

    + *
  • Time Complexity: O(n²) + *
  • Space Complexity: O(n²) + *
+ */ +class MemoizedSolution { + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + // TODO: Implement memoized solution + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java deleted file mode 100644 index c769da1..0000000 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java +++ /dev/null @@ -1,52 +0,0 @@ -package codes.yam.leetcode.minimumdeletionstomakestringbalanced; - -/** - * Naive solution for the Minimum Deletions to Make String Balanced problem. - * - *

Tries every split point and counts misplaced characters at each. - * - *

    - *
  • Time Complexity: O(n²) - *
  • Space Complexity: O(1) - *
- */ -class NaiveSolution { - /** - * Counts misplaced characters around the given split point in {@code s}. - * - * @param s the string of {@code 'a'} and {@code 'b'} characters - * @param index the split point index - * @return an array {@code [bCount, aCount]} where {@code bCount} is the number of {@code 'b'}s - * before {@code index} and {@code aCount} is the number of {@code 'a'}s after {@code index} - */ - int[] getSliceInfo(String s, int index) { - int aCount = 0, bCount = 0; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == 'b' && i < index) { - bCount++; - } - if (c == 'a' && i > index) { - aCount++; - } - } - return new int[] {bCount, aCount}; - } - - /** - * Returns the minimum number of deletions to make {@code s} balanced. - * - * @param s the string of {@code 'a'} and {@code 'b'} characters - * @return the minimum number of deletions needed - */ - int minimumDeletions(String s) { - int n = s.length(); - int min = n; - for (int i = 0; i < n; i++) { - int[] info = getSliceInfo(s, i); - int computed = info[0] + info[1]; - min = Math.min(min, computed); - } - return min; - } -} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java deleted file mode 100644 index 2956294..0000000 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java +++ /dev/null @@ -1,52 +0,0 @@ -package codes.yam.leetcode.minimumdeletionstomakestringbalanced; - -/** - * Prefix/suffix solution for the Minimum Deletions to Make String Balanced problem. - * - *

Pre-computes prefix {@code 'b'} counts and suffix {@code 'a'} counts, then finds the optimal - * split point in a single pass. - * - *

    - *
  • Time Complexity: O(n) - *
  • Space Complexity: O(n) - *
- */ -class PrefixSuffixSolution { - /** - * Returns the minimum number of deletions to make {@code s} balanced. - * - * @param s the string of {@code 'a'} and {@code 'b'} characters - * @return the minimum number of deletions needed - */ - int minimumDeletions(String s) { - var aCountList = new int[s.length()]; - var bCountList = new int[s.length()]; - int aCount = 0; - int bCount = 0; - int n = s.length(); - int min = n; - - // count a's from right to left - for (int i = n - 1; i >= 0; i--) { - aCountList[i] = aCount; - if (s.charAt(i) == 'a') { - aCount++; - } - } - - // count b's from left to right - for (int i = 0; i < n; i++) { - bCountList[i] = bCount; - if (s.charAt(i) == 'b') { - bCount++; - } - } - - // calculate minimum - for (int i = 0; i < n; i++) { - min = Math.min(min, aCountList[i] + bCountList[i]); - } - - return min; - } -} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java new file mode 100644 index 0000000..da62c39 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java @@ -0,0 +1,24 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Recursive solution for the Minimum Deletions to Make String Balanced problem. + * + *

At each character, choose to delete it or keep it, exploring all possibilities. + * + *

    + *
  • Time Complexity: O(2^n) + *
  • Space Complexity: O(n) (call stack) + *
+ */ +class RecursiveSolution { + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + // TODO: Implement recursive solution + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java index 09e874c..e46d59e 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java @@ -3,15 +3,12 @@ /** * Solution for the Minimum Deletions to Make String Balanced problem. * - *

Uses a single-pass DP approach, tracking the minimum deletions with constant space. + *

Optimizes {@code DpSolution} by collapsing the DP table into constant space. * *

    *
  • Time Complexity: O(n) *
  • Space Complexity: O(1) *
- * - * @see NaiveSolution - * @see PrefixSuffixSolution */ class Solution { /** @@ -21,7 +18,7 @@ class Solution { * @return the minimum number of deletions needed */ int minimumDeletions(String s) { - // TODO: Implement O(1) space DP solution + // TODO: Implement space-optimized DP solution throw new UnsupportedOperationException("Not yet implemented"); } } diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index 995bf88..0415b64 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -9,10 +9,12 @@ *

Solution progression: * *

    - *
  1. {@code NaiveSolution} — Brute force, O(n²) time, O(1) space - *
  2. {@code PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) + *
  3. {@code RecursiveSolution} — Brute force recursion, O(2^n) time, O(n) * space - *
  4. {@code Solution} — Single-pass DP, O(n) time, O(1) space + *
  5. {@code MemoizedSolution} — Memoized recursion, O(n²) time, O(n²) + * space + *
  6. {@code DpSolution} — Bottom-up DP, O(n) time, O(n) space + *
  7. {@code Solution} — Space-optimized DP, O(n) time, O(1) space *
* * @see
Problem diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java index eda2e9c..b7bd676 100644 --- a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java @@ -22,14 +22,20 @@ static Stream cases() { @ParameterizedTest @MethodSource("cases") - void naiveSolution(String s, int expected) { - assertEquals(expected, new NaiveSolution().minimumDeletions(s)); + void recursiveSolution(String s, int expected) { + assertEquals(expected, new RecursiveSolution().minimumDeletions(s)); } @ParameterizedTest @MethodSource("cases") - void prefixSuffixSolution(String s, int expected) { - assertEquals(expected, new PrefixSuffixSolution().minimumDeletions(s)); + void memoizedSolution(String s, int expected) { + assertEquals(expected, new MemoizedSolution().minimumDeletions(s)); + } + + @ParameterizedTest + @MethodSource("cases") + void dpSolution(String s, int expected) { + assertEquals(expected, new DpSolution().minimumDeletions(s)); } @ParameterizedTest From d6a3ef028b0ef21ce2770855678dd6870a4981b0 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Feb 2026 00:57:20 +0500 Subject: [PATCH 05/26] Refactor solution progression documentation and add naive and prefix/suffix tests --- .../NaiveSolution.java | 52 +++++++++++++++++++ .../PrefixSuffixSolution.java | 52 +++++++++++++++++++ .../package-info.java | 11 +++- .../SolutionTest.java | 12 +++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java create mode 100644 src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java new file mode 100644 index 0000000..c769da1 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java @@ -0,0 +1,52 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Naive solution for the Minimum Deletions to Make String Balanced problem. + * + *

Tries every split point and counts misplaced characters at each. + * + *

    + *
  • Time Complexity: O(n²) + *
  • Space Complexity: O(1) + *
+ */ +class NaiveSolution { + /** + * Counts misplaced characters around the given split point in {@code s}. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @param index the split point index + * @return an array {@code [bCount, aCount]} where {@code bCount} is the number of {@code 'b'}s + * before {@code index} and {@code aCount} is the number of {@code 'a'}s after {@code index} + */ + int[] getSliceInfo(String s, int index) { + int aCount = 0, bCount = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == 'b' && i < index) { + bCount++; + } + if (c == 'a' && i > index) { + aCount++; + } + } + return new int[] {bCount, aCount}; + } + + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + int n = s.length(); + int min = n; + for (int i = 0; i < n; i++) { + int[] info = getSliceInfo(s, i); + int computed = info[0] + info[1]; + min = Math.min(min, computed); + } + return min; + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java new file mode 100644 index 0000000..2956294 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java @@ -0,0 +1,52 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +/** + * Prefix/suffix solution for the Minimum Deletions to Make String Balanced problem. + * + *

Pre-computes prefix {@code 'b'} counts and suffix {@code 'a'} counts, then finds the optimal + * split point in a single pass. + * + *

    + *
  • Time Complexity: O(n) + *
  • Space Complexity: O(n) + *
+ */ +class PrefixSuffixSolution { + /** + * Returns the minimum number of deletions to make {@code s} balanced. + * + * @param s the string of {@code 'a'} and {@code 'b'} characters + * @return the minimum number of deletions needed + */ + int minimumDeletions(String s) { + var aCountList = new int[s.length()]; + var bCountList = new int[s.length()]; + int aCount = 0; + int bCount = 0; + int n = s.length(); + int min = n; + + // count a's from right to left + for (int i = n - 1; i >= 0; i--) { + aCountList[i] = aCount; + if (s.charAt(i) == 'a') { + aCount++; + } + } + + // count b's from left to right + for (int i = 0; i < n; i++) { + bCountList[i] = bCount; + if (s.charAt(i) == 'b') { + bCount++; + } + } + + // calculate minimum + for (int i = 0; i < n; i++) { + min = Math.min(min, aCountList[i] + bCountList[i]); + } + + return min; + } +} diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index 0415b64..adae77a 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -6,7 +6,16 @@ *
  • Difficulty: Medium * * - *

    Solution progression: + *

    Solution progression (split-point approach): + * + *

      + *
    1. {@code NaiveSolution} — Brute force split-point, O(n²) time, O(1) + * space + *
    2. {@code PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) + * space + *
    + * + *

    Solution progression (DP approach): * *

      *
    1. {@code RecursiveSolution} — Brute force recursion, O(2^n) time, O(n) diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java index b7bd676..3482607 100644 --- a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java @@ -20,6 +20,18 @@ static Stream cases() { arguments("bbbbaaaa", 4)); } + @ParameterizedTest + @MethodSource("cases") + void naiveSolution(String s, int expected) { + assertEquals(expected, new NaiveSolution().minimumDeletions(s)); + } + + @ParameterizedTest + @MethodSource("cases") + void prefixSuffixSolution(String s, int expected) { + assertEquals(expected, new PrefixSuffixSolution().minimumDeletions(s)); + } + @ParameterizedTest @MethodSource("cases") void recursiveSolution(String s, int expected) { From e00ba3de510dc6b4f960e6c2c5fc5fd5fdfe3608 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Fri, 13 Feb 2026 20:50:51 +0500 Subject: [PATCH 06/26] Refactor: Rename solution classes and update references --- CLAUDE.md | 24 +++++++-- .../Solution.java | 3 +- .../{DpSolution.java => SolutionDp.java} | 3 +- ...zedSolution.java => SolutionMemoized.java} | 5 +- ...{NaiveSolution.java => SolutionNaive.java} | 2 +- ...olution.java => SolutionPrefixSuffix.java} | 2 +- ...veSolution.java => SolutionRecursive.java} | 3 +- .../package-info.java | 10 ++-- .../SolutionDpTest.java | 14 ++++++ .../SolutionMemoizedTest.java | 14 ++++++ .../SolutionNaiveTest.java | 14 ++++++ .../SolutionPrefixSuffixTest.java | 14 ++++++ .../SolutionRecursiveTest.java | 14 ++++++ .../SolutionTest.java | 50 ++----------------- .../TestCases.java | 21 ++++++++ 15 files changed, 129 insertions(+), 64 deletions(-) rename src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/{DpSolution.java => SolutionDp.java} (93%) rename src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/{MemoizedSolution.java => SolutionMemoized.java} (85%) rename src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/{NaiveSolution.java => SolutionNaive.java} (98%) rename src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/{PrefixSuffixSolution.java => SolutionPrefixSuffix.java} (97%) rename src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/{RecursiveSolution.java => SolutionRecursive.java} (93%) create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDpTest.java create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoizedTest.java create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaiveTest.java create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffixTest.java create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursiveTest.java create mode 100644 src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/TestCases.java diff --git a/CLAUDE.md b/CLAUDE.md index b3a0b7e..5bba2f1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,15 +17,27 @@ Each LeetCode problem lives in its own package under `codes.yam.leetcode`: ``` src/main/java/codes/yam/leetcode/{problemslug}/ -├── Solution.java # The solution implementation -└── package-info.java # Problem metadata (slug, difficulty, link) +├── Solution.java # The final/optimal solution +├── SolutionNaive.java # Additional solutions (prefix naming: Solution*.java) +├── SolutionDp.java +└── package-info.java # Problem metadata + solution progression ``` -Tests go under `src/test/java/codes/yam/leetcode/{problemslug}/`. +Tests go under `src/test/java/codes/yam/leetcode/{problemslug}/`: + +``` +src/test/java/codes/yam/leetcode/{problemslug}/ +├── TestCases.java # Shared test data (static Stream cases()) +├── SolutionTest.java # Tests Solution.java +├── SolutionNaiveTest.java # Tests SolutionNaive.java (one test file per solution) +└── SolutionDpTest.java +``` + +Each test file uses `@MethodSource("fully.qualified.TestCases#cases")` to share test data. ## Code Conventions -**Solution classes** are package-scoped (not `public`), named `Solution`, and use HTML-formatted Javadoc: +**Solution classes** are package-scoped (not `public`), use prefix naming (`Solution`, `SolutionNaive`, `SolutionDp`, etc.), and use HTML-formatted Javadoc: ```java /** @@ -35,7 +47,9 @@ Tests go under `src/test/java/codes/yam/leetcode/{problemslug}/`. *
    2. Space Complexity: O(...)
    3. * */ -class Solution { ... } +class Solution { + // ... +} ``` **package-info.java** documents problem metadata with the same HTML style: diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java index e46d59e..d92eda0 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java @@ -3,7 +3,7 @@ /** * Solution for the Minimum Deletions to Make String Balanced problem. * - *

      Optimizes {@code DpSolution} by collapsing the DP table into constant space. + *

      Optimizes {@code SolutionDp} by collapsing the DP table into constant space. * *

        *
      • Time Complexity: O(n) @@ -17,6 +17,7 @@ class Solution { * @param s the string of {@code 'a'} and {@code 'b'} characters * @return the minimum number of deletions needed */ + @SuppressWarnings("unused") int minimumDeletions(String s) { // TODO: Implement space-optimized DP solution throw new UnsupportedOperationException("Not yet implemented"); diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDp.java similarity index 93% rename from src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java rename to src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDp.java index 1c7f6ed..453d052 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/DpSolution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDp.java @@ -10,13 +10,14 @@ *
      • Space Complexity: O(n) *
      */ -class DpSolution { +class SolutionDp { /** * Returns the minimum number of deletions to make {@code s} balanced. * * @param s the string of {@code 'a'} and {@code 'b'} characters * @return the minimum number of deletions needed */ + @SuppressWarnings("unused") int minimumDeletions(String s) { // TODO: Implement bottom-up DP solution throw new UnsupportedOperationException("Not yet implemented"); diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoized.java similarity index 85% rename from src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java rename to src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoized.java index e871e87..63f3a34 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/MemoizedSolution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoized.java @@ -3,20 +3,21 @@ /** * Memoized solution for the Minimum Deletions to Make String Balanced problem. * - *

      Same recursive approach as {@code RecursiveSolution}, but caches overlapping subproblems. + *

      Same recursive approach as {@code SolutionRecursive}, but caches overlapping subproblems. * *

        *
      • Time Complexity: O(n²) *
      • Space Complexity: O(n²) *
      */ -class MemoizedSolution { +class SolutionMemoized { /** * Returns the minimum number of deletions to make {@code s} balanced. * * @param s the string of {@code 'a'} and {@code 'b'} characters * @return the minimum number of deletions needed */ + @SuppressWarnings("unused") int minimumDeletions(String s) { // TODO: Implement memoized solution throw new UnsupportedOperationException("Not yet implemented"); diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaive.java similarity index 98% rename from src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java rename to src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaive.java index c769da1..796524a 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/NaiveSolution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaive.java @@ -10,7 +10,7 @@ *
    4. Space Complexity: O(1) * */ -class NaiveSolution { +class SolutionNaive { /** * Counts misplaced characters around the given split point in {@code s}. * diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffix.java similarity index 97% rename from src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java rename to src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffix.java index 2956294..dcd7c01 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/PrefixSuffixSolution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffix.java @@ -11,7 +11,7 @@ *
    5. Space Complexity: O(n) * */ -class PrefixSuffixSolution { +class SolutionPrefixSuffix { /** * Returns the minimum number of deletions to make {@code s} balanced. * diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursive.java similarity index 93% rename from src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java rename to src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursive.java index da62c39..02e6330 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/RecursiveSolution.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursive.java @@ -10,13 +10,14 @@ *
    6. Space Complexity: O(n) (call stack) * */ -class RecursiveSolution { +class SolutionRecursive { /** * Returns the minimum number of deletions to make {@code s} balanced. * * @param s the string of {@code 'a'} and {@code 'b'} characters * @return the minimum number of deletions needed */ + @SuppressWarnings("unused") int minimumDeletions(String s) { // TODO: Implement recursive solution throw new UnsupportedOperationException("Not yet implemented"); diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index adae77a..1fdedb8 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -9,20 +9,20 @@ *

      Solution progression (split-point approach): * *

        - *
      1. {@code NaiveSolution} — Brute force split-point, O(n²) time, O(1) + *
      2. {@code SolutionNaive} — Brute force split-point, O(n²) time, O(1) * space - *
      3. {@code PrefixSuffixSolution} — Prefix/suffix arrays, O(n) time, O(n) + *
      4. {@code SolutionPrefixSuffix} — Prefix/suffix arrays, O(n) time, O(n) * space *
      * *

      Solution progression (DP approach): * *

        - *
      1. {@code RecursiveSolution} — Brute force recursion, O(2^n) time, O(n) + *
      2. {@code SolutionRecursive} — Brute force recursion, O(2^n) time, O(n) * space - *
      3. {@code MemoizedSolution} — Memoized recursion, O(n²) time, O(n²) + *
      4. {@code SolutionMemoized} — Memoized recursion, O(n²) time, O(n²) * space - *
      5. {@code DpSolution} — Bottom-up DP, O(n) time, O(n) space + *
      6. {@code SolutionDp} — Bottom-up DP, O(n) time, O(n) space *
      7. {@code Solution} — Space-optimized DP, O(n) time, O(1) space *
      * diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDpTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDpTest.java new file mode 100644 index 0000000..2af0264 --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionDpTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionDpTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { + assertEquals(expected, new SolutionDp().minimumDeletions(s)); + } +} diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoizedTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoizedTest.java new file mode 100644 index 0000000..0e2f51e --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionMemoizedTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionMemoizedTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { + assertEquals(expected, new SolutionMemoized().minimumDeletions(s)); + } +} diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaiveTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaiveTest.java new file mode 100644 index 0000000..5dd7a09 --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionNaiveTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionNaiveTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { + assertEquals(expected, new SolutionNaive().minimumDeletions(s)); + } +} diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffixTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffixTest.java new file mode 100644 index 0000000..cd9031e --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionPrefixSuffixTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionPrefixSuffixTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { + assertEquals(expected, new SolutionPrefixSuffix().minimumDeletions(s)); + } +} diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursiveTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursiveTest.java new file mode 100644 index 0000000..712f83f --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionRecursiveTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionRecursiveTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { + assertEquals(expected, new SolutionRecursive().minimumDeletions(s)); + } +} diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java index 3482607..5d286f4 100644 --- a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/SolutionTest.java @@ -1,58 +1,14 @@ package codes.yam.leetcode.minimumdeletionstomakestringbalanced; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.params.provider.Arguments.arguments; -import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -public class SolutionTest { - static Stream cases() { - return Stream.of( - arguments("aababbab", 2), - arguments("bbaaaaabb", 2), - arguments("b", 0), - arguments("ba", 1), - arguments("aabbbaa", 2), - arguments("abababab", 3), - arguments("bbbbaaaa", 4)); - } - - @ParameterizedTest - @MethodSource("cases") - void naiveSolution(String s, int expected) { - assertEquals(expected, new NaiveSolution().minimumDeletions(s)); - } - - @ParameterizedTest - @MethodSource("cases") - void prefixSuffixSolution(String s, int expected) { - assertEquals(expected, new PrefixSuffixSolution().minimumDeletions(s)); - } - - @ParameterizedTest - @MethodSource("cases") - void recursiveSolution(String s, int expected) { - assertEquals(expected, new RecursiveSolution().minimumDeletions(s)); - } - - @ParameterizedTest - @MethodSource("cases") - void memoizedSolution(String s, int expected) { - assertEquals(expected, new MemoizedSolution().minimumDeletions(s)); - } - - @ParameterizedTest - @MethodSource("cases") - void dpSolution(String s, int expected) { - assertEquals(expected, new DpSolution().minimumDeletions(s)); - } - +class SolutionTest { @ParameterizedTest - @MethodSource("cases") - void solution(String s, int expected) { + @MethodSource("codes.yam.leetcode.minimumdeletionstomakestringbalanced.TestCases#cases") + void minimumDeletions(String s, int expected) { assertEquals(expected, new Solution().minimumDeletions(s)); } } diff --git a/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/TestCases.java b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/TestCases.java new file mode 100644 index 0000000..0f66f8f --- /dev/null +++ b/src/test/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/TestCases.java @@ -0,0 +1,21 @@ +package codes.yam.leetcode.minimumdeletionstomakestringbalanced; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +/** Shared test cases for all solutions to the Minimum Deletions problem. */ +@SuppressWarnings("unused") // Referenced via @MethodSource strings +public class TestCases { + static Stream cases() { + return Stream.of( + arguments("aababbab", 2), + arguments("bbaaaaabb", 2), + arguments("b", 0), + arguments("ba", 1), + arguments("aabbbaa", 2), + arguments("abababab", 3), + arguments("bbbbaaaa", 4)); + } +} From c874624724db11aa2aa445ee6be49e0a128a5ba6 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Fri, 13 Feb 2026 20:58:04 +0500 Subject: [PATCH 07/26] Refactor solution file naming and test structure --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b0de941..8da1b61 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,21 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms. ``` src/main/java/codes/yam/leetcode/{problem-slug}/ - Solution.java # Optimal solution - RecursiveSolution.java # Brute-force recursion (optional) - MemoizedSolution.java # Memoized recursion (optional) - DpSolution.java # Bottom-up DP (optional) - package-info.java # Problem metadata & solution progression + Solution.java # Optimal/final solution + SolutionNaive.java # Additional solutions (prefix naming: Solution*.java) + SolutionDp.java + package-info.java # Problem metadata & solution progression src/test/java/codes/yam/leetcode/{problem-slug}/ - SolutionTest.java # Unit tests - SolutionBenchmark.java # JMH benchmarks + TestCases.java # Shared test data (static Stream cases()) + SolutionTest.java # Tests Solution.java + SolutionNaiveTest.java # One test file per solution + SolutionBenchmark.java # JMH benchmarks (optional) ``` -When a problem has multiple solution approaches, each gets its own class. `Solution.java` is always the optimal/final -version. The progression is documented in `package-info.java` and all solutions share the same test cases. +When a problem has multiple solution approaches, each gets its own `Solution*.java` class. `Solution.java` is always the +optimal/final version. The progression is documented in `package-info.java`. Test data lives in a shared `TestCases.java`, +and each solution has its own test file using `@MethodSource` to reference it. ## Commands From 0ebe23f79b4a09d2d255397d85d89f4b27a662b3 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Fri, 13 Feb 2026 21:03:47 +0500 Subject: [PATCH 08/26] Refactor Test run configuration encoding --- .idea/runConfigurations/Test.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/runConfigurations/Test.xml b/.idea/runConfigurations/Test.xml index 17e1f74..f6e43d4 100644 --- a/.idea/runConfigurations/Test.xml +++ b/.idea/runConfigurations/Test.xml @@ -16,4 +16,4 @@
    7. {@code Solution} — Space-optimized DP, O(n) time, O(1) space *
    * + *

    TODO: Current {@code Solution} uses a split-point-derived approach. Once the DP + * progression is implemented, replace it with the cleaner DP formulation (rename current to {@code + * SolutionSplitPointOptimized}). + * * @see Problem * Link */ From ae5de78f3e2ca295d60abc57f8202b4b1a54a3c4 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 21:35:23 +0500 Subject: [PATCH 15/26] Update TODO comment for DP implementation in minimum deletions --- .../minimumdeletionstomakestringbalanced/package-info.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java index bc8e409..6f3c1ae 100644 --- a/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java +++ b/src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/package-info.java @@ -26,9 +26,9 @@ *

  • {@code Solution} — Space-optimized DP, O(n) time, O(1) space * * - *

    TODO: Current {@code Solution} uses a split-point-derived approach. Once the DP - * progression is implemented, replace it with the cleaner DP formulation (rename current to {@code - * SolutionSplitPointOptimized}). + *

    TODO: Current {@code Solution} uses a split-point approach. Once the DP progression is + * implemented, replace it with the cleaner DP formulation (rename current to {@code + * SolutionSplitPoint}). * * @see Problem * Link From 9916c30af77225e43d106ea77e43d1f0c6ecd4d3 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 21:48:08 +0500 Subject: [PATCH 16/26] Add climbing stairs solution and test cases --- CLAUDE.md | 7 ++++++- .../yam/leetcode/climbingstairs/Solution.java | 7 +++++++ .../yam/leetcode/climbingstairs/SolutionTest.java | 14 ++++++++++++++ .../yam/leetcode/climbingstairs/TestCases.java | 11 +++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/main/java/codes/yam/leetcode/climbingstairs/Solution.java create mode 100644 src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java create mode 100644 src/test/java/codes/yam/leetcode/climbingstairs/TestCases.java diff --git a/CLAUDE.md b/CLAUDE.md index 5bba2f1..0679eca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,9 +35,14 @@ src/test/java/codes/yam/leetcode/{problemslug}/ Each test file uses `@MethodSource("fully.qualified.TestCases#cases")` to share test data. +**When to use `@ValueSource` vs `@MethodSource`:** Use `@ValueSource` for single-argument problems with groupable +outcomes (e.g., boolean results — group inputs by true/false). Use `@MethodSource` with `TestCases` when each input has +a distinct expected output (e.g., `climbStairs(3) → 3`). + ## Code Conventions -**Solution classes** are package-scoped (not `public`), use prefix naming (`Solution`, `SolutionNaive`, `SolutionDp`, etc.), and use HTML-formatted Javadoc: +**Solution classes** are package-scoped (not `public`), use prefix naming (`Solution`, `SolutionNaive`, `SolutionDp`, +etc.), and use HTML-formatted Javadoc: ```java /** diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java new file mode 100644 index 0000000..34cab3b --- /dev/null +++ b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java @@ -0,0 +1,7 @@ +package codes.yam.leetcode.climbingstairs; + +public class Solution { + public int climbStairs(int n) { + return n; + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java new file mode 100644 index 0000000..9aba5ad --- /dev/null +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.climbingstairs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") + void climbStairs(int n, int expected) { + assertEquals(expected, new Solution().climbStairs(n)); + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/TestCases.java b/src/test/java/codes/yam/leetcode/climbingstairs/TestCases.java new file mode 100644 index 0000000..57b1644 --- /dev/null +++ b/src/test/java/codes/yam/leetcode/climbingstairs/TestCases.java @@ -0,0 +1,11 @@ +package codes.yam.leetcode.climbingstairs; + +import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +@SuppressWarnings("unused") +class TestCases { + static Stream cases() { + return Stream.of(Arguments.of(2, 2), Arguments.of(3, 3)); + } +} From 6a031ed746454c2f00951003b83e9e4c6857ee81 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 21:54:23 +0500 Subject: [PATCH 17/26] Clarify `@ValueSource` and `@MethodSource` usage in README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 890b974..4511f41 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ src/test/java/codes/yam/leetcode/{problem-slug}/ ``` When a problem has multiple solution approaches, each gets its own `Solution*.java` class. `Solution.java` is always the -optimal/final version. The progression is documented in `package-info.java`. Test data lives in a shared `TestCases.java`, +optimal/final version. The progression is documented in `package-info.java`. Test data lives in a shared +`TestCases.java`, and each solution has its own test file using `@MethodSource` to reference it. ## Commands @@ -51,7 +52,8 @@ Right-click `src/test/java/codes/yam/leetcode/` → **New → Package** → `pro Start with LeetCode's example cases plus a few edge cases (aim for 3–5 per behavior): -**For single-arg problems** — use `@ValueSource`, grouped by expected outcome: +**For single-argument problems with groupable outcomes** (e.g., boolean results — group inputs by true/false) — use +`@ValueSource`: ```java class SolutionTest { @@ -71,7 +73,7 @@ class SolutionTest { } ``` -**For multi-arg problems** — use `@MethodSource`: +**When each input has a distinct expected output** (e.g., `climbStairs(3) → 3`) — use `@MethodSource` with `TestCases`: ```java class SolutionTest { From 7d0add9e597633a2f0b91a5b31d3c1618ca8dd94 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 21:57:39 +0500 Subject: [PATCH 18/26] Disable climbing stairs tests until implementation is complete --- .../yam/leetcode/climbingstairs/SolutionDp.java | 15 +++++++++++++++ .../climbingstairs/SolutionMemoized.java | 15 +++++++++++++++ .../climbingstairs/SolutionRecursive.java | 15 +++++++++++++++ .../leetcode/climbingstairs/SolutionDpTest.java | 16 ++++++++++++++++ .../climbingstairs/SolutionMemoizedTest.java | 16 ++++++++++++++++ .../climbingstairs/SolutionRecursiveTest.java | 14 ++++++++++++++ .../leetcode/climbingstairs/SolutionTest.java | 2 ++ 7 files changed, 93 insertions(+) create mode 100644 src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java create mode 100644 src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java create mode 100644 src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java create mode 100644 src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java create mode 100644 src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java create mode 100644 src/test/java/codes/yam/leetcode/climbingstairs/SolutionRecursiveTest.java diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java new file mode 100644 index 0000000..c3e5c78 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java @@ -0,0 +1,15 @@ +package codes.yam.leetcode.climbingstairs; + +/** + * Solution for the Climbing Stairs problem. + * + *

      + *
    • Time Complexity: O(n) + *
    • Space Complexity: O(n) + *
    + */ +class SolutionDp { + int climbStairs(int n) { + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java new file mode 100644 index 0000000..6c6e285 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java @@ -0,0 +1,15 @@ +package codes.yam.leetcode.climbingstairs; + +/** + * Solution for the Climbing Stairs problem. + * + *
      + *
    • Time Complexity: O(n) + *
    • Space Complexity: O(n) + *
    + */ +class SolutionMemoized { + int climbStairs(int n) { + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java new file mode 100644 index 0000000..ed65e9f --- /dev/null +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java @@ -0,0 +1,15 @@ +package codes.yam.leetcode.climbingstairs; + +/** + * Solution for the Climbing Stairs problem. + * + *
      + *
    • Time Complexity: O(2^n) + *
    • Space Complexity: O(n) + *
    + */ +class SolutionRecursive { + int climbStairs(int n) { + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java new file mode 100644 index 0000000..3c0ec0a --- /dev/null +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java @@ -0,0 +1,16 @@ +package codes.yam.leetcode.climbingstairs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +@Disabled("Not yet implemented") +class SolutionDpTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") + void climbStairs(int n, int expected) { + assertEquals(expected, new SolutionDp().climbStairs(n)); + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java new file mode 100644 index 0000000..982ba43 --- /dev/null +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java @@ -0,0 +1,16 @@ +package codes.yam.leetcode.climbingstairs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +@Disabled("Not yet implemented") +class SolutionMemoizedTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") + void climbStairs(int n, int expected) { + assertEquals(expected, new SolutionMemoized().climbStairs(n)); + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionRecursiveTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionRecursiveTest.java new file mode 100644 index 0000000..042be7d --- /dev/null +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionRecursiveTest.java @@ -0,0 +1,14 @@ +package codes.yam.leetcode.climbingstairs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SolutionRecursiveTest { + @ParameterizedTest + @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") + void climbStairs(int n, int expected) { + assertEquals(expected, new SolutionRecursive().climbStairs(n)); + } +} diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java index 9aba5ad..11cb176 100644 --- a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java @@ -2,9 +2,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +@Disabled("Not yet implemented") class SolutionTest { @ParameterizedTest @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") From 9ff2fde55be1efa85c39c5a003ee2e5c19ccbe74 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 22:02:37 +0500 Subject: [PATCH 19/26] Implement recursive solution for climbing stairs problem --- .../codes/yam/leetcode/climbingstairs/SolutionRecursive.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java index ed65e9f..7f4f502 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionRecursive.java @@ -10,6 +10,8 @@ */ class SolutionRecursive { int climbStairs(int n) { - throw new UnsupportedOperationException("Not yet implemented"); + if (n == 1) return 1; + if (n == 2) return 2; + return climbStairs(n - 1) + climbStairs(n - 2); } } From d910277e76806a408187e68c7e217d87d679ad61 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 22:12:31 +0500 Subject: [PATCH 20/26] Implement memoized solution for climbing stairs problem --- .../yam/leetcode/climbingstairs/SolutionMemoized.java | 11 ++++++++++- .../leetcode/climbingstairs/SolutionMemoizedTest.java | 2 -- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java index 6c6e285..70e83d9 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java @@ -9,7 +9,16 @@ * */ class SolutionMemoized { + int rec(int n) { + int[] memo = new int[n + 1]; + memo[1] = 1; + memo[2] = 2; + if (memo[n - 1] == 0) memo[n - 1] = rec(n - 1); + if (memo[n - 2] == 0) memo[n - 2] = rec(n - 2); + return memo[n - 1] + memo[n - 2]; + } + int climbStairs(int n) { - throw new UnsupportedOperationException("Not yet implemented"); + return rec(n); } } diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java index 982ba43..cc841b6 100644 --- a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionMemoizedTest.java @@ -2,11 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -@Disabled("Not yet implemented") class SolutionMemoizedTest { @ParameterizedTest @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") From bfb216e409843b6687225ce80f77ea4635affb01 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 22:14:03 +0500 Subject: [PATCH 21/26] Add base cases for n=1 and n=2 to memoized solution --- .../codes/yam/leetcode/climbingstairs/SolutionMemoized.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java index 70e83d9..3330e76 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java @@ -19,6 +19,8 @@ int rec(int n) { } int climbStairs(int n) { + if (n == 1) return 1; + if (n == 2) return 2; return rec(n); } } From 9fc6b12d5d8e46e9ba8b8e541fa3021890ae52d7 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 22:17:04 +0500 Subject: [PATCH 22/26] Initialize memoization array as a class member --- .../climbingstairs/SolutionMemoized.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java index 3330e76..6229501 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionMemoized.java @@ -9,18 +9,19 @@ * */ class SolutionMemoized { + int[] memo; + int rec(int n) { - int[] memo = new int[n + 1]; - memo[1] = 1; - memo[2] = 2; - if (memo[n - 1] == 0) memo[n - 1] = rec(n - 1); - if (memo[n - 2] == 0) memo[n - 2] = rec(n - 2); - return memo[n - 1] + memo[n - 2]; + if (n == 1) return 1; + if (n == 2) return 2; + if (memo[n] == 0) { + memo[n] = rec(n - 1) + rec(n - 2); + } + return memo[n]; } int climbStairs(int n) { - if (n == 1) return 1; - if (n == 2) return 2; + memo = new int[n + 1]; return rec(n); } } From ab1871cd59d561e0062b9f90c267567b059ddb04 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 23:41:03 +0500 Subject: [PATCH 23/26] Implement dynamic programming solution for climbing stairs --- .../yam/leetcode/climbingstairs/NOTES.md | 61 +++++++++++++++++++ .../yam/leetcode/climbingstairs/Solution.java | 16 ++++- .../leetcode/climbingstairs/SolutionDp.java | 9 ++- .../climbingstairs/SolutionDpTest.java | 2 - .../leetcode/climbingstairs/SolutionTest.java | 2 - 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 src/main/java/codes/yam/leetcode/climbingstairs/NOTES.md diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/NOTES.md b/src/main/java/codes/yam/leetcode/climbingstairs/NOTES.md new file mode 100644 index 0000000..55e5c82 --- /dev/null +++ b/src/main/java/codes/yam/leetcode/climbingstairs/NOTES.md @@ -0,0 +1,61 @@ +# Recursion to DP: Step by Step + +## Starting point: your memoized solution + +```java +int rec(int n) { + if (n == 1) return 1; + if (n == 2) return 2; + if (memo[n] != 0) return memo[n]; + memo[n] = rec(n - 1) + rec(n - 2); + return memo[n]; +} +``` + +## Steps to convert to bottom-up DP + +1. **Look at what the memo array stores.** Each `memo[i]` holds the answer for input `i`. That's your DP table. + +2. **Find the base cases.** These are the entries you can fill without recursion: `memo[1] = 1` and `memo[2] = 2`. + +3. **Find the recurrence.** Strip away the recursion and just look at the formula: + `memo[n] = memo[n - 1] + memo[n - 2]`. + +4. **Figure out the fill order.** `memo[i]` depends on `memo[i - 1]` and `memo[i - 2]` — both smaller. So fill from + small to large (3, 4, 5, ... n). + +5. **Write the loop.** Seed the base cases, then loop from 3 to n applying the recurrence. + +## Result + +```java +int climbStairs(int n) { + if (n <= 2) return n; + int[] dp = new int[n + 1]; + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} +``` + +## Bonus: space optimization + +Notice `dp[i]` only ever reads `dp[i - 1]` and `dp[i - 2]`. You don't need the whole array — just two variables. + +```java +int climbStairs(int n) { + if (n <= 2) return n; + int prev2 = 1, prev1 = 2; + for (int i = 3; i <= n; i++) { + int curr = prev1 + prev2; + prev2 = prev1; + prev1 = curr; + } + return prev1; +} +``` + +This drops space from O(n) to O(1). diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java index 34cab3b..4233153 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java @@ -1,7 +1,17 @@ package codes.yam.leetcode.climbingstairs; -public class Solution { - public int climbStairs(int n) { - return n; +class Solution { + int climbStairs(int n) { + if (n == 1) return 1; + if (n == 2) return 2; + int a = 1; + int b = 2; + int c = a + b; + for (int i = 4; i <= n; i++) { + c = a + b; + a = b; + b = c; + } + return c; } } diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java index c3e5c78..0bc112a 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java @@ -10,6 +10,13 @@ */ class SolutionDp { int climbStairs(int n) { - throw new UnsupportedOperationException("Not yet implemented"); + int[] dp = new int[n + 1]; + // ignore dp[0] + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; } } diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java index 3c0ec0a..6aaedf3 100644 --- a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionDpTest.java @@ -2,11 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -@Disabled("Not yet implemented") class SolutionDpTest { @ParameterizedTest @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") diff --git a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java index 11cb176..9aba5ad 100644 --- a/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java +++ b/src/test/java/codes/yam/leetcode/climbingstairs/SolutionTest.java @@ -2,11 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -@Disabled("Not yet implemented") class SolutionTest { @ParameterizedTest @MethodSource("codes.yam.leetcode.climbingstairs.TestCases#cases") From 5de63dd70d0d8ccbc84bb68fd59d95226c8bd8aa Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 23:42:04 +0500 Subject: [PATCH 24/26] Make Solution class public and adjust loop start index --- src/main/java/codes/yam/leetcode/climbingstairs/Solution.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java index 4233153..92e6bb8 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java @@ -1,13 +1,13 @@ package codes.yam.leetcode.climbingstairs; -class Solution { +public class Solution { int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; int a = 1; int b = 2; int c = a + b; - for (int i = 4; i <= n; i++) { + for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; From 2a6cb8a5d188ae625a6147dd88150d7be369a557 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 23:46:27 +0500 Subject: [PATCH 25/26] Add Climbing Stairs to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4511f41..1115d2b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms. |------|-----------------------------------------------------------------------------------------------------------------------|------------|-------------------|--------| | 1 | [Two Sum](https://leetcode.com/problems/two-sum/) | Easy | `O(n log n)` | `O(n)` | | 9 | [Palindrome Number](https://leetcode.com/problems/palindrome-number/) | Easy | `O(log10(n) / 2)` | `O(1)` | +| 70 | [Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) | Easy | `O(n)` | `O(1)` | | 1653 | [Minimum Deletions to Make String Balanced](https://leetcode.com/problems/minimum-deletions-to-make-string-balanced/) | Medium | `O(n)` | `O(1)` | ## Project Structure From f747d7eea2aa4c0366d166f8e5a2051c6092125c Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Feb 2026 23:48:09 +0500 Subject: [PATCH 26/26] Refactor Solution to O(1) space complexity --- .../codes/yam/leetcode/climbingstairs/Solution.java | 12 +++++++++++- .../yam/leetcode/climbingstairs/SolutionDp.java | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java index 92e6bb8..55fba14 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/Solution.java @@ -1,6 +1,16 @@ package codes.yam.leetcode.climbingstairs; -public class Solution { +/** + * Solution for the Climbing Stairs problem. + * + *

    Optimizes {@code SolutionDp} by replacing the DP array with two rolling variables. + * + *

      + *
    • Time Complexity: O(n) + *
    • Space Complexity: O(1) + *
    + */ +class Solution { int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; diff --git a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java index 0bc112a..0807e5c 100644 --- a/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java +++ b/src/main/java/codes/yam/leetcode/climbingstairs/SolutionDp.java @@ -10,8 +10,8 @@ */ class SolutionDp { int climbStairs(int n) { + if (n <= 2) return n; int[] dp = new int[n + 1]; - // ignore dp[0] dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) {