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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A comprehensive collection of coding challenges from multiple platforms for lear

| Platform | Focus Area | Problems Solved |
| ------------------------------- | ---------------------------- | --------------- |
| [LeetCode](#leetcode) | Data Structures & Algorithms | 173 |
| [LeetCode](#leetcode) | Data Structures & Algorithms | 174 |
| [GreatFrontEnd](#greatfrontend) | Frontend Engineering | 15 |

## Platforms
Expand Down Expand Up @@ -175,7 +175,7 @@ Various places where I enjoy practicing solving coding challenges:
</details>

<details>
<summary>Medium Problems (77 solved)</summary>
<summary>Medium Problems (78 solved)</summary>

### Stack & Design

Expand Down Expand Up @@ -222,6 +222,7 @@ Various places where I enjoy practicing solving coding challenges:
- [0187 - Repeated DNA Sequences](./leetcode/medium/0187-repeated-dna-sequences) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0340 - Longest Substring with At Most K Distinct Characters](./leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0395 - Longest Substring with At Least K Repeating Characters](./leetcode/medium/0395-longest-substring-with-at-least-k-repeating-characters) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0424 - Longest Repeating Character Replacement](./leetcode/medium/0424-longest-repeating-character-replacement) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0438 - Find All Anagrams in a String](./leetcode/medium/0438-find-all-anagrams-in-a-string) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0616 - Add Bold Tag in String](./leetcode/medium/0616-add-bold-tag-in-string) ![Medium](https://img.shields.io/badge/Medium-orange)
- [0692 - Top K Frequent Words](./leetcode/medium/0692-top-k-frequent-words) ![Medium](https://img.shields.io/badge/Medium-orange)
Expand Down Expand Up @@ -334,6 +335,7 @@ Various places where I enjoy practicing solving coding challenges:
- [0159 - Longest Substring with At Most Two Distinct Characters](./leetcode/medium/0159-longest-substring-with-at-most-two-distinct-characters) - Medium
- [0209 - Minimum Size Subarray Sum](./leetcode/medium/0209-minimum-size-subarray-sum) - Medium
- [0340 - Longest Substring with At Most K Distinct Characters](./leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters) - Medium
- [0424 - Longest Repeating Character Replacement](./leetcode/medium/0424-longest-repeating-character-replacement) - Medium
- [1084 - Find K-Length Substrings With No Repeated Characters](./leetcode/medium/1084-find-k-length-substrings-with-no-repeated-characters) - Medium
- [More in docs →](./docs/techniques/SLIDING_WINDOW.md)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Post-Mortem Log

## Problem

**[Longest Repeating Character Replacement](https://leetcode.com/problems/longest-repeating-character-replacement/)** (LeetCode #424)

Given a string `s` of uppercase English letters and an integer `k`, find the length of the longest substring containing the same letter after performing at most `k` character replacements.

- **Problem Name:** Longest Repeating Character Replacement
- **Problem Link:** <https://leetcode.com/problems/longest-repeating-character-replacement/>
- **Date:** 2026-02-06

---

## Time Tracking

- **Time to design the algorithm:** ~5 minutes (pattern recognition was straightforward)
- **Time to code:** ~15 minutes (5 TDD cycles, incremental implementation)
- **Time debugging/fixing:** ~5 minutes (minimal - TDD caught issues early)

---

## Solution Exploration

### What approaches did I consider?

1. **Brute Force** - Check all possible substrings
- For each starting position, try all ending positions
- For each substring, count characters and check if replacements ≤ k
- Time: O(n²) or O(n³) depending on implementation
- Space: O(1) or O(26)

2. **Optimized Sliding Window** (Final Solution)
- Use two pointers to maintain a window
- Track character frequencies in the current window
- A window is valid when: `window_length - max_frequency ≤ k`
- Expand right pointer, shrink left when invalid
- Time: O(n), Space: O(1)

### Final Solution Analysis

- **Time Complexity:** O(n) - single pass through the string with two pointers
- **Space Complexity:** O(1) - frequency map has at most 26 uppercase letters (constant)
- **Is it optimal?** Yes - we must examine each character at least once, so O(n) is optimal

### Why this approach worked (or didn't)

The sliding window approach works because:

- We only need to track the maximum frequency character in each window
- The key insight is that `window_length - max_frequency` tells us how many replacements we need
- As we slide the window, we maintain this invariant: needed replacements ≤ k
- We don't need to recalculate max frequency from scratch when shrinking - we just use the running max

**Trade-offs:**

- Sliding window requires careful boundary management
- But it's much more efficient than brute force
- The "trick" is realizing we don't need to recalculate maxFreq when shrinking (using the historical max is sufficient)

---

## Pattern Recognition

### What algorithmic pattern does this problem use?

**Variable-Size Sliding Window** with constraint tracking

### Key Insight

The "aha moment" is realizing that a window is valid if and only if:

```text
window_length - max_frequency_in_window ≤ k
```

This formula tells us: "If I keep the most common character and replace all others, do I stay within k replacements?"

We don't need to track which character to keep or replace - just count frequencies and apply this formula.

### Related Problems

1. **LeetCode 3: Longest Substring Without Repeating Characters**
- Similar sliding window pattern, different constraint

2. **LeetCode 1004: Max Consecutive Ones III**
- Nearly identical problem structure (can flip at most k zeros)

3. **LeetCode 340: Longest Substring with At Most K Distinct Characters**
- Variable sliding window with k constraint

4. **LeetCode 209: Minimum Size Subarray Sum**
- Variable sliding window finding minimum instead of maximum

---

## Edge Cases & Verification

### What clarifying questions did I ask?

- What if k = 0? (No replacements allowed)
- What if k is very large? (Can replace entire string)
- What if the string is already all one character?
- What about single-character strings?

### Edge cases handled

- ✅ All characters different, k=0 → returns 1
- ✅ All characters the same → returns string length
- ✅ Single character string → returns 1
- ✅ k larger than needed → returns full string length
- ✅ Both example cases from problem statement

### Edge cases missed

- None identified - test coverage appears comprehensive

---

## Mistakes & Bugs

### Mistakes I keep making

- Initial hardcoded return in Cycle 1 was intentional (TDD principle)
- No significant bugs encountered during implementation
- The TDD approach prevented common sliding window mistakes:
- Off-by-one errors in window size calculation
- Forgetting to update frequency map when shrinking window
- Not handling edge cases incrementally

### Bugs to add to the Bug List

- N/A - clean implementation from the start due to TDD

---

## Retrospective

### Key Takeaways & Lessons Learned

1. **TDD forces simplicity first** - Starting with a hardcoded value felt silly but led to a natural progression
2. **The window validity formula is the key** - Once you understand `len - maxFreq ≤ k`, implementation is straightforward
3. **Edge case testing prevented bugs** - Adding tests for k=0, single char, all same chars caught potential issues early
4. **Pattern recognition accelerates solving** - Recognizing this as a sliding window problem immediately suggested the approach
5. **TDD cycles build confidence** - Each passing test confirmed the algorithm was on the right track

### Add to Cheat Sheet

**Variable Sliding Window Template:**

```typescript
function slidingWindow(s: string, k: number): number {
const freq: Record<string, number> = {};
let maxFreq = 0;
let maxResult = 0;
let left = 0;

for (let right = 0; right < s.length; right++) {
// Expand: add right element
freq[s[right]] = (freq[s[right]] || 0) + 1;
maxFreq = Math.max(maxFreq, freq[s[right]]);

// Shrink: while window is invalid
while (WINDOW_INVALID_CONDITION) {
freq[s[left]]--;
left++;
}

// Update result
maxResult = Math.max(maxResult, right - left + 1);
}

return maxResult;
}
```

**Key Formula for this problem:**

- Window is valid when: `windowLength - maxFreq ≤ k`
- Window size: `right - left + 1`

---

## Rubric Self-Rating (1–5)

| Category | Rating | Notes |
| ------------------- | ------ | ---------------------------------------------------------------------- |
| **Problem solving** | 5 | Correctly identified pattern, understood key insight, optimal solution |
| **Coding** | 5 | Clean implementation, good variable names, proper TypeScript types |
| **Verification** | 5 | Comprehensive test coverage, all edge cases handled |
| **Communication** | 5 | Clear test names, well-commented code, followed TDD discipline |

---
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# [Longest Repeating Character Replacement](https://leetcode.com/problems/longest-repeating-character-replacement) ![Medium](https://img.shields.io/badge/Medium-orange)

You are given a string `s` and an integer `k`. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most `k` times.

Return _the length of the longest substring containing the same letter you can get after performing the above operations_.

**Example 1:**

```bash
Input: s = "ABAB", k = 2
Output: 4
Explanation: Replace the two 'A's with two 'B's or vice versa.
```

**Example 2:**

```bash
Input: s = "AABABBA", k = 1
Output: 4
Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA".
The substring "BBBB" has the longest repeating letters, which is 4.
```

**Constraints:**

- `1 <= s.length <= 10^5`
- `s` consists of only uppercase English letters.
- `0 <= k <= s.length`
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, it, expect } from 'vitest';
import { characterReplacement } from './longest-repeating-character-replacement';

describe('Longest Repeating Character Replacement', () => {
it('should return 4 for "ABAB" with k=2', () => {
expect(characterReplacement('ABAB', 2)).toBe(4);
});

it('should return 4 for "AABABBA" with k=1', () => {
expect(characterReplacement('AABABBA', 1)).toBe(4);
});

it('should return 1 when k=0 and all characters are different', () => {
expect(characterReplacement('ABCD', 0)).toBe(1);
});

it('should return full length when all characters are the same', () => {
expect(characterReplacement('AAAA', 0)).toBe(4);
});

it('should handle single character string', () => {
expect(characterReplacement('A', 1)).toBe(1);
});

it('should return full string length when k is large enough to replace all', () => {
expect(characterReplacement('ABCDE', 10)).toBe(5);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Finds the length of the longest substring with the same letter after at most k replacements
* @param s - Input string consisting of uppercase English letters
* @param k - Maximum number of character replacements allowed
* @returns Length of the longest valid substring
*/
export function characterReplacement(s: string, k: number): number {
const charCount: Record<string, number> = {};
let maxFreq = 0;
let maxLength = 0;
let left = 0;

for (let right = 0; right < s.length; right++) {
// Add current character to window
charCount[s[right]] = (charCount[s[right]] || 0) + 1;

// Update max frequency in current window
maxFreq = Math.max(maxFreq, charCount[s[right]]);

// If window is invalid (need more than k replacements), shrink from left
while (right - left + 1 - maxFreq > k) {
charCount[s[left]]--;
left++;
}

// Update max length
maxLength = Math.max(maxLength, right - left + 1);
}

return maxLength;
}
Loading