Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
abcbdc8
Add IntelliJ IDEA run configurations to gitignore and JUnit launcher …
yamcodes Feb 10, 2026
5d18af7
Merge branch 'main' into 18-improve-minimum-deletions-space-complexit…
yamcodes Feb 10, 2026
1fe4740
Add solution progression to package-info and stub O(1) DP solution
yamcodes Feb 12, 2026
0ef02a2
Refactor solution links to use code tags for clarity
yamcodes Feb 12, 2026
87750a9
Update solution progression to reflect new implementations
yamcodes Feb 12, 2026
d6a3ef0
Refactor solution progression documentation and add naive and prefix/…
yamcodes Feb 12, 2026
e00ba3d
Refactor: Rename solution classes and update references
yamcodes Feb 13, 2026
c874624
Refactor solution file naming and test structure
yamcodes Feb 13, 2026
0ebe23f
Refactor Test run configuration encoding
yamcodes Feb 13, 2026
bebf22e
Rename test run configuration to "Test All"
yamcodes Feb 13, 2026
808d20e
Implement space-optimized DP solution for minimum deletions
yamcodes Feb 14, 2026
dccc212
Refactor minimumDeletions to simplify logic and improve clarity
yamcodes Feb 14, 2026
3022aaf
Refactor variable initialization for clarity
yamcodes Feb 14, 2026
357059e
Disable unused test classes and reorder variables in Solution.java
yamcodes Feb 14, 2026
4d737f4
Add TODO to refactor Solution with cleaner DP formulation
yamcodes Feb 14, 2026
ae5de78
Update TODO comment for DP implementation in minimum deletions
yamcodes Feb 14, 2026
d75ca68
Refactor to use constant space with split-point counting
yamcodes Feb 14, 2026
9819eef
Update time complexity for Minimum Deletions to String Balanced
yamcodes Feb 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 19 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arguments> 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
/**
Expand All @@ -35,7 +47,9 @@ Tests go under `src/test/java/codes/yam/leetcode/{problemslug}/`.
* <li><b>Space Complexity:</b> <code>O(...)</code></li>
* </ul>
*/
class Solution { ... }
class Solution {
// ...
}
```

**package-info.java** documents problem metadata with the same HTML style:
Expand Down
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ 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)` |
| 1653 | [Minimum Deletions to Make String Balanced](https://leetcode.com/problems/minimum-deletions-to-make-string-balanced/) | Medium | `O(n)` | `O(n)` |
| 1653 | [Minimum Deletions to Make String Balanced](https://leetcode.com/problems/minimum-deletions-to-make-string-balanced/) | Medium | `O(n)` | `O(1)` |

## Project Structure

```
src/main/java/codes/yam/leetcode/{problem-slug}/
Solution.java # Solution implementation
package-info.java # Problem metadata
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<Arguments> 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 `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

```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,107 +3,35 @@
/**
* Solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>Uses a split-point counting approach to compute minimum deletions in constant space.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(1)</code>
* </ul>
*/
class Solution {
/**
* Counts misplaced characters around the given split point in {@code s}.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(1)</code>
* </ul>
*
* @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.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n²)</code>
* <li><b>Space Complexity:</b> <code>O(1)</code>
* </ul>
*
* @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.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(n)</code>
* </ul>
* 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;
int aAfter = 0;
int bBefore = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == 'b') {
bCount++;
} else {
aAfter++;
}
if (aAfter + bBefore > bCount) {
bBefore = bCount;
aAfter = 0;
}
}

// calculate minimum
for (int i = 0; i < n; i++) {
min = Math.min(min, aCountList[i] + bCountList[i]);
}

return min;
return Math.min(bCount, aAfter + bBefore);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package codes.yam.leetcode.minimumdeletionstomakestringbalanced;

/**
* Bottom-up DP solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>Flips the memoized recursion into an iterative DP table.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(n)</code>
* </ul>
*/
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");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package codes.yam.leetcode.minimumdeletionstomakestringbalanced;

/**
* Memoized solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>Same recursive approach as {@code SolutionRecursive}, but caches overlapping subproblems.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n²)</code>
* <li><b>Space Complexity:</b> <code>O(n²)</code>
* </ul>
*/
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");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package codes.yam.leetcode.minimumdeletionstomakestringbalanced;

/**
* Naive solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>Tries every split point and counts misplaced characters at each.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n²)</code>
* <li><b>Space Complexity:</b> <code>O(1)</code>
* </ul>
*/
class SolutionNaive {
/**
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package codes.yam.leetcode.minimumdeletionstomakestringbalanced;

/**
* Prefix/suffix solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>Pre-computes prefix {@code 'b'} counts and suffix {@code 'a'} counts, then finds the optimal
* split point in a single pass.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(n)</code>
* <li><b>Space Complexity:</b> <code>O(n)</code>
* </ul>
*/
class SolutionPrefixSuffix {
/**
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package codes.yam.leetcode.minimumdeletionstomakestringbalanced;

/**
* Recursive solution for the <b>Minimum Deletions to Make String Balanced</b> problem.
*
* <p>At each character, choose to delete it or keep it, exploring all possibilities.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(2^n)</code>
* <li><b>Space Complexity:</b> <code>O(n)</code> (call stack)
* </ul>
*/
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");
}
}
Loading