diff --git a/README.md b/README.md index c44ec94..1129add 100644 --- a/README.md +++ b/README.md @@ -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 @@ -175,7 +175,7 @@ Various places where I enjoy practicing solving coding challenges:
-Medium Problems (77 solved) +Medium Problems (78 solved) ### Stack & Design @@ -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) @@ -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) diff --git a/leetcode/medium/0424-longest-repeating-character-replacement/POST_MORTEM.md b/leetcode/medium/0424-longest-repeating-character-replacement/POST_MORTEM.md new file mode 100644 index 0000000..6169178 --- /dev/null +++ b/leetcode/medium/0424-longest-repeating-character-replacement/POST_MORTEM.md @@ -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:** +- **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 = {}; + 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 | + +--- diff --git a/leetcode/medium/0424-longest-repeating-character-replacement/README.md b/leetcode/medium/0424-longest-repeating-character-replacement/README.md new file mode 100644 index 0000000..a75fffb --- /dev/null +++ b/leetcode/medium/0424-longest-repeating-character-replacement/README.md @@ -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` diff --git a/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.test.ts b/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.test.ts new file mode 100644 index 0000000..9cbf419 --- /dev/null +++ b/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.test.ts @@ -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); + }); +}); diff --git a/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.ts b/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.ts new file mode 100644 index 0000000..d02fc9e --- /dev/null +++ b/leetcode/medium/0424-longest-repeating-character-replacement/longest-repeating-character-replacement.ts @@ -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 = {}; + 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; +}