Skip to content

Conversation

@pertrai1
Copy link
Owner

@pertrai1 pertrai1 commented Dec 30, 2025

Summary by CodeRabbit

  • New Features

    • Added LeetCode Problem 0340: Longest Substring with At Most K Distinct Characters to the Sliding Window section.
  • Documentation

    • Reformatted problem documentation with improved Markdown structure and clearer example formatting.

✏️ Tip: You can customize this high-level summary in your review settings.

@pertrai1 pertrai1 requested a review from Copilot December 30, 2025 13:34
@pertrai1 pertrai1 added the code challenge this is a coding challenge label Dec 30, 2025
@continue
Copy link

continue bot commented Dec 30, 2025

All Green - Keep your PRs mergeable

Learn more

All Green is an AI agent that automatically:

✅ Addresses code review comments

✅ Fixes failing CI checks

✅ Resolves merge conflicts


Unsubscribe from All Green comments

@github-actions
Copy link

@codex review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Files were reorganized from a root-level directory structure to a nested leetcode/medium/ subdirectory. The problem statement README was reformatted with Markdown code blocks. The TypeScript solution was moved and exported. The main README was updated to reference the new location in two sections.

Changes

Cohort / File(s) Summary
Old location deletions
0340-longest-substring-with-at-most-k-distinct-characters/README.md, 0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
Removed from root-level problem directory; migrated to nested structure under leetcode/medium/
New location additions
leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md
Problem statement reformatted: replaced HTML-based examples and constraints with Markdown code blocks and bullet-style representation
New location code
leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
Sliding-window algorithm moved and exported; added inline documentation clarifying time/space complexity; identical runtime behavior preserved
Main README updates
README.md
Added problem entry in two locations: "Problems by Difficulty" > Sliding Window and "Problems by Pattern" > Sliding Window sections, both pointing to new leetcode/medium/ path

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A hop, skip, and restructured bound,
From scattered roots to ordered ground!
Code finds home in medium stacks,
Windows sliding on new tracks—
Organized chaos, solved at last!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: reorganizing and refactoring the LeetCode problem 0340 by moving it to a new directory structure and making the function exported.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch leetcode-12302025

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1b5a71 and 0d870b2.

📒 Files selected for processing (5)
  • 0340-longest-substring-with-at-most-k-distinct-characters/README.md
  • 0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
  • README.md
  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md
  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
💤 Files with no reviewable changes (2)
  • 0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
  • 0340-longest-substring-with-at-most-k-distinct-characters/README.md
🧰 Additional context used
📓 Path-based instructions (5)
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Create comprehensive documentation guides for arrays, trees, graphs, heaps, and other data structures
Document reusable code templates for common algorithmic patterns
Maintain Big O complexity cheatsheets and references

Files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md
  • README.md
**/README.md

📄 CodeRabbit inference engine (CLAUDE.md)

Each problem directory should include a README.md containing: problem title and difficulty badge, link to the problem on the respective platform, problem description, examples and constraints

Files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md
  • README.md
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ts: Use explicit type annotations for function parameters and return types in TypeScript solutions
Follow strict TypeScript configuration with ES2020 target and CommonJS modules in TypeScript solutions
Enforce full type safety in TypeScript solutions

Files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts}: Use clear, descriptive variable names across all solutions
Add comments for non-obvious logic or algorithm steps in code
Keep functions focused and single-purpose across all solutions
Prefer readability over cleverness in code implementation

**/*.{js,ts}: Use descriptive variable names; single letters like i, j, k are acceptable for loop iterators and standard mathematical contexts
Add comments for complex logic, but prefer self-documenting code with clear logic
Minimize external dependencies and keep solutions self-contained

**/*.{js,ts}: Use clear, descriptive variable names (not just i, j, k unless in simple loops) and add comments for non-obvious logic or algorithm steps
Keep functions focused and single-purpose, preferring readability over cleverness
Use const by default, let only when reassignment needed; avoid var except for LeetCode solution function definitions
Use modern ES6+ syntax (arrow functions, destructuring, spread operators) where appropriate
Prefer built-in methods (.map(), .filter(), .reduce()) when appropriate and use strict equality (===) over loose equality (==)

Files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use explicit type annotations for function parameters and return types in TypeScript
Follow strict TypeScript configuration as defined in tsconfig.json with ES2020 target and CommonJS modules

Files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/* : Organize problem directories as `[number]-[problem-name]` (e.g., `0001-two-sum`) with README.md and solution files named `[problem-name].{js,ts}`
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.{js,ts} : Don't deviate from LeetCode's expected function signatures in problem solution files
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.md : Problem README files should include: problem title with difficulty badge, problem description from LeetCode, examples with inputs/outputs, and constraints
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T23:55:10.338Z
Learning: Applies to **/*.js : Keep JavaScript solutions concise and focused on the algorithm
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T17:56:01.747Z
Learning: Applies to **/README.md : Each problem directory should include a README.md containing: problem title and difficulty badge, link to the problem on the respective platform, problem description, examples and constraints
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.{js,ts} : Use standard algorithmic patterns (two pointers, sliding window, hash maps, DFS, BFS, DP) and leverage appropriate data structures (Map, Set, arrays)
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.{js,ts} : Keep solutions concise and focused on the algorithm without over-engineering
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T23:55:10.338Z
Learning: Applies to **/*.{js,ts} : Use clear, descriptive variable names across all solutions
📚 Learning: 2025-11-26T14:46:46.097Z
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.md : Problem README files should include: problem title with difficulty badge, problem description from LeetCode, examples with inputs/outputs, and constraints

Applied to files:

  • leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md
📚 Learning: 2025-12-02T17:56:01.747Z
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T17:56:01.747Z
Learning: When reviewing array/string problems: check for two-pointer technique correctness, verify sliding window boundaries, and confirm hash map usage is optimal

Applied to files:

  • README.md
📚 Learning: 2025-11-26T14:46:46.097Z
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T14:46:46.097Z
Learning: Applies to [0-9]*-*/*.{js,ts} : Use standard algorithmic patterns (two pointers, sliding window, hash maps, DFS, BFS, DP) and leverage appropriate data structures (Map, Set, arrays)

Applied to files:

  • README.md
📚 Learning: 2025-11-24T23:55:10.338Z
Learnt from: CR
Repo: pertrai1/coding-challenges PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T23:55:10.338Z
Learning: Identify and document which algorithmic patterns the solution uses (Two Pointers, Sliding Window, DFS/BFS, DP, etc.)

Applied to files:

  • README.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: review
🔇 Additional comments (10)
README.md (2)

228-228: LGTM! Correct path reference for reorganized problem.

The entry correctly points to the new nested directory structure and follows the established naming convention.


331-331: LGTM! Problem correctly categorized under Sliding Window pattern.

The entry appropriately places this problem in the Sliding Window section, which aligns with the algorithm used in the solution.

leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md (3)

1-3: LGTM! Well-formatted problem header.

The title includes a proper link to the LeetCode problem and a difficulty badge. The description is clear and properly formatted.


5-19: LGTM! Examples are well-formatted and comprehensive.

The examples properly demonstrate the problem with clear inputs, outputs, and explanations using markdown code blocks as per coding guidelines.


21-24: LGTM! Constraints are clearly documented.

The constraints are properly formatted and provide necessary bounds for the problem parameters.

leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts (5)

1-6: LGTM! Accurate complexity analysis.

The JSDoc comment correctly documents O(n) time complexity and O(k) space complexity with clear explanations of the reasoning.


7-8: LGTM! Proper export and edge case handling.

The function is correctly exported with explicit TypeScript type annotations as per coding guidelines. The edge case for k === 0 is properly handled.


10-12: LGTM! Appropriate data structures and clear naming.

Uses Map for character frequency tracking (optimal for this sliding window problem) with clear, descriptive variable names following coding guidelines.


14-33: LGTM! Correct sliding window implementation.

The algorithm properly implements the sliding window pattern:

  • Expands window by adding characters and updating frequencies
  • Shrinks window when distinct character count exceeds k
  • Correctly removes characters from the map when their count drops to zero
  • Updates max length appropriately

The non-null assertion on line 23 is safe in this context since leftChar is guaranteed to exist in the map. The inline comments clearly explain each step of the algorithm.


35-36: LGTM! Clean return statement.

The function correctly returns the maximum substring length found.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

@codex review

@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

1 similar comment
@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@github-actions
Copy link

github-actions bot commented Dec 30, 2025

Claude finished @pertrai1's task in 1m 16s —— View job


Code Review: Longest Substring With At Most K Distinct Characters

Excellent work on this solution! This is a clean implementation of the sliding window pattern with proper complexity analysis and clear comments.

Todo List

  • Read solution implementation
  • Read problem description
  • Analyze algorithm correctness
  • Verify complexity analysis
  • Identify pattern and key insights
  • Review code quality
  • Suggest related problems
  • Provide learning summary

✅ Algorithm Correctness

The solution correctly handles all test cases and edge cases:

  • Empty string: Handled by the loop (returns 0)
  • k = 0: Correctly returns 0 (line 8)
  • Single character: Works correctly (window expands to length 1)
  • All distinct characters with k < length: Window shrinks appropriately
  • All same characters: Window expands to full string length

Mental trace for Example 1 (s = "eceba", k = 2):

  • Window expands: eecece (max=3, 2 distinct)
  • Add b: eceb (3 distinct, exceeds k)
  • Shrink: cebeb (2 distinct)
  • Add a: eba (3 distinct, exceeds k)
  • Shrink: ba (2 distinct)
  • Result: 3 ✓

✅ Complexity Analysis

Your complexity analysis is accurate and well-explained:

Time: O(n)

  • Each character is visited at most twice (once by right, once by left)
  • The while loop appears nested, but the left pointer never resets
  • Amortized O(1) operations for Map get/set/delete
  • Your comment correctly explains this: "each character visited at most twice by sliding window pointers"

Space: O(k)

  • The map stores at most k + 1 characters before the window shrinks
  • Your comment accurately captures this: "the map stores at most k + 1 characters at any time before shrinking"

🎯 Pattern Recognition: Sliding Window (Variable Size)

Pattern: Variable-size sliding window with HashMap frequency tracking

Why this pattern?
This problem requires finding the longest valid substring, which is a hallmark of variable sliding window problems. The constraint (≤ k distinct characters) can be maintained as we expand the window, and we only shrink when violated.

Key Insight:
The non-obvious trick here is that we expand greedily and only shrink when necessary. Many beginners try to track "exactly k" distinct characters, but the problem asks for "at most k", meaning we should maximize the window size as long as we don't exceed k distinct characters.

Additionally, using characterCount.size to track distinct characters in O(1) time is more elegant than maintaining a separate counter.


💻 Code Quality

Strengths:

  • ✅ Excellent variable names (characterCount, maxLength, leftChar)
  • ✅ Clear comments explaining each major step
  • ✅ Proper use of const and let
  • ✅ Non-null assertion (!) is safe here since we know the key exists
  • ✅ Modern ES6+ syntax with Map
  • ✅ Good formatting and indentation
  • ✅ Added complexity documentation at the top
  • ✅ Added export keyword for better module structure

Minor observations (not issues):

  • The comment "Expand the window by moving the right pointer" (line 14) is slightly redundant since the for loop makes this clear, but it's good for educational purposes
  • Using characterCount.get(char) || 0 is fine, though TypeScript purists might prefer ?? 0 for nullish coalescing

Improvements from previous version:

  • ✅ Added comprehensive JSDoc-style complexity comments
  • ✅ Added inline comments for each step
  • ✅ Better formatting (consistent 2-space indentation)
  • ✅ README now uses clean markdown instead of HTML

🔗 Related Problems

Practice these similar variable sliding window problems to reinforce the pattern:

  1. LeetCode 3: Longest Substring Without Repeating Characters (Medium)

    • Nearly identical pattern, but with k = n (all distinct)
    • Great for understanding the "expand and shrink" strategy
  2. LeetCode 904: Fruit Into Baskets (Medium)

    • This is literally the same problem with k=2, just reworded
    • Excellent for pattern recognition practice
  3. LeetCode 159: Longest Substring with At Most Two Distinct Characters (Medium)

    • Special case of this problem where k=2
    • Allows optimization with a simpler tracking mechanism
  4. LeetCode 992: Subarrays with K Different Integers (Hard)

    • Next level: count subarrays instead of finding longest
    • Uses the insight: exactlyK = atMostK - atMost(K-1)

📚 Learning Summary

What makes this solution work?

  1. Greedy expansion: Always try to expand the window as much as possible
  2. Lazy contraction: Only shrink when the constraint is violated
  3. Efficient tracking: Using Map.size gives O(1) distinct character count
  4. Two-pointer invariant: left never resets, ensuring O(n) time

Common mistakes to avoid:

  • ❌ Trying to maintain "exactly k" distinct characters (problem says "at most k")
  • ❌ Resetting the window completely when k is exceeded (unnecessary, just shrink from left)
  • ❌ Forgetting to delete map entries when count reaches 0 (causes incorrect size)
  • ❌ Thinking the nested while makes it O(n²) (it's amortized O(n) because left never goes backward)

When to use this pattern?

  • Finding longest/shortest substring/subarray with a certain property
  • Constraint can be maintained/checked efficiently as window changes
  • Problem asks for "at most" or "at least" some condition

🎉 Verdict

This is a textbook-quality implementation of the sliding window pattern. The code is correct, efficient, well-commented, and easy to understand. Great job on the improvements from the previous version!

Final Score: ⭐⭐⭐⭐⭐ (5/5)


@github-actions
Copy link

github-actions bot commented Dec 30, 2025

🧮 Solution Analysis

PR: LeetCode Longest Substring With At Most K Distinct Characters
Files Changed: 0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts

📊 Code Quality Results

ESLint Analysis

🔍 Complexity Analysis

💡 Analysis Tips

  • Time Complexity: Focus on the dominant operation in loops
  • Space Complexity: Consider auxiliary data structures
  • Optimization: Look for opportunities to improve efficiency
  • Edge Cases: Ensure your solution handles empty inputs, null/undefined, edge conditions

📁 Analysis Files Created

Detailed analysis files have been automatically generated and committed to docs/analysis/ for future reference.

🎯 Next Steps

  1. Review any ESLint errors/warnings above
  2. Consider the complexity analysis - is this the optimal approach?
  3. Test with edge cases mentioned in the problem description
  4. Add comments explaining complex logic
  5. Check the docs/analysis/ directory for detailed complexity analysis files

Analysis generated for PR files only

@github-actions
Copy link

✅ Test Results

Tests have been executed for this pull request.

📊 Coverage Summary

Metric Coverage
Statements 93.54%
Branches 87.8%
Functions 82.35%
Lines 93.64%

Check the workflow logs for detailed test output.

@github-actions
Copy link


🤖 Gemini Code Review (Model: gemini-2.5-flash)

Here's a detailed review of the provided LeetCode solution for "Longest Substring with At Most K Distinct Characters":


1. Pattern Identification

This solution effectively uses the Sliding Window algorithmic pattern. Specifically, it's a dynamic sliding window where the window size adjusts based on a condition (number of distinct characters).

2. Complexity Verification

  • Time Complexity: O(N) where N is the length of the input string s.

    • Verification: The right pointer iterates through the string exactly once. The left pointer also moves forward at most N times across the entire execution (it never moves backward). Each character is added to the map once and removed from the map at most once. Map operations (get, set, delete) take O(1) on average. Therefore, the total time complexity is linear, O(N).
    • Conclusion: The stated time complexity O(n) is accurate.
  • Space Complexity: O(k) where k is the maximum number of distinct characters allowed in the substring.

    • Verification: The characterCount Map stores the frequency of characters within the current window. At any point, this map will contain at most k + 1 distinct characters (before the window is shrunk). Given the problem constraint 0 <= k <= 50, the map size is bounded by k (or the size of the alphabet, whichever is smaller). Since k is a variable input, O(k) is an accurate way to express the space used by the map.
    • Conclusion: The stated space complexity O(k) is accurate.

3. Key Insight

The key insight for solving this problem efficiently is to use a sliding window combined with a frequency map (or hash map).

  1. Window Expansion: We expand the window to the right, adding new characters and updating their counts in the frequency map.
  2. Window Contraction (Conditional): If, after adding a character, the number of distinct characters in our window (characterCount.size) exceeds k, we must shrink the window from the left. We decrement the count of the character at the left pointer. If its count drops to zero, it means that character is no longer present in the window, so we remove it from the map, effectively reducing the distinct character count. We continue shrinking until the distinct character count is back to k or less.
  3. Maximum Length Tracking: At each step where the window is valid (i.e., characterCount.size <= k), we calculate the current window's length (right - left + 1) and update maxLength if it's larger.

This approach ensures that we always maintain a "valid" window (at most k distinct characters) and efficiently find the longest such window.

4. Edge Case Analysis

The solution handles several edge cases correctly:

  • k = 0: The code explicitly checks if (k === 0) return 0;. This is correct, as a substring with 0 distinct characters is impossible (unless the string itself is empty, but s.length >= 1).
  • s length 1:
    • If s = "a", k = 0: Returns 0 (handled by early exit).
    • If s = "a", k = 1: characterCount becomes {"a": 1}. maxLength becomes 1. Correctly returns 1.
  • All characters in s are the same (e.g., s = "aaaaa", k = 1): The while loop condition characterCount.size > k will never be true. maxLength will correctly be updated to s.length.
  • k is greater than or equal to the total distinct characters in s (e.g., s = "abc", k = 3): The while loop will never execute, and the entire string will be considered, resulting in maxLength = s.length.
  • Constraints: 1 <= s.length <= 5 * 10^4, 0 <= k <= 50. The O(N) time complexity handles the string length, and O(k) space handles the k constraint efficiently.

The solution appears robust for the given constraints and typical edge cases.

5. Learning Points

  • Similar problems using this pattern:

  • Common mistakes people make with this pattern:

    • Incorrect window length calculation: For a window defined by left and right (inclusive), the length is right - left + 1. A common mistake is right - left.
    • Failing to remove characters from the frequency map: When shrinking the window, if a character's count drops to zero, it must be removed from the map to accurately reflect the number of distinct characters.
    • Off-by-one errors in pointer movement: Ensuring left and right pointers are initialized and incremented correctly.
    • Not handling k=0 or empty string cases: Although constraints might prevent empty strings, k=0 is a valid input that needs special handling.
    • Using an array instead of a map when character set is large/unknown: While an array can be faster for small, fixed alphabets (like ASCII), a Map is more general and robust for arbitrary character sets or large Unicode ranges.
  • Variations of this problem:

    • Finding the count of such substrings/subarrays instead of the length of the longest.
    • Finding the shortest substring with exactly k distinct characters.
    • Modifying the condition (e.g., "at least k distinct characters", "exactly k distinct characters").

6. Code Quality

The code quality is excellent:

  • Variable Naming: characterCount, left, right, maxLength, char, leftChar are all clear and descriptive, enhancing readability.
  • Code Structure: The logic flows naturally: initialization, expanding loop, conditional shrinking loop, and updating the result.
  • Readability: Comments explaining the purpose of the pointers and the overall logic (Expand the window, Shrink the window) are very helpful. The time and space complexity comments are also well-placed and accurate.
  • TypeScript Best Practices:
    • Type annotations (Map<string, number>) are used correctly.
    • The non-null assertion operator ! on characterCount.get(leftChar)! is safe here because leftChar must exist in the map to be decremented.
    • The export keyword was added, which is good for module organization and testing.
    • The README.md file was also updated to a more standard and readable format.

7. Alternative Approaches

  1. Brute Force (Inefficient):

    • Approach: Generate all possible substrings of s. For each substring, count the number of distinct characters. If it's k or less, compare its length with the current maximum.
    • Trade-offs:
      • Time Complexity: Generating O(N^2) substrings. For each substring of length up to N, counting distinct characters takes O(N) (e.g., using a Set or Map). This leads to an O(N^3) overall time complexity.
      • Space Complexity: O(N) for storing frequency maps for each substring.
      • This approach is too slow for the given constraints (N up to 5 * 10^4).
  2. Optimized Brute Force with Pre-computation (Still Suboptimal):

    • Approach: Instead of re-counting distinct characters for each substring, one could pre-compute distinct counts for all prefixes or use a more clever approach to extend distinct counts. However, iterating through all O(N^2) substrings is still the bottleneck.
    • Trade-offs:
      • Time Complexity: Still O(N^2) because of iterating through all possible start and end points of substrings.
      • Space Complexity: O(AlphabetSize) for frequency tracking per substring.
      • Better than O(N^3) but still too slow for large N.

The sliding window approach is the optimal one for this problem, offering a significant performance improvement over brute-force methods.

@github-actions
Copy link


🤖 Gemini Code Review (Model: gemini-2.5-flash)

Here's a detailed review of the provided LeetCode solution for "Longest Substring with At Most K Distinct Characters":


1. Pattern Identification

This solution uses the Sliding Window algorithmic pattern. Specifically, it's a variable-size sliding window.

2. Complexity Verification

  • Time Complexity: O(n) where n is the length of the input string s.

    • Verification: The right pointer iterates through the string once, from 0 to s.length - 1. The left pointer also moves forward, and each character enters and leaves the window at most once. All Map operations (set, get, delete, size) take O(1) time on average. Therefore, the total time complexity is linear, O(n).
    • Accuracy: The stated time complexity O(n) is accurate.
  • Space Complexity: O(k) where k is the maximum number of distinct characters allowed.

    • Verification: The characterCount Map stores the frequency of characters currently within the sliding window. At any point, this map will store at most k + 1 distinct characters (before the window shrinks) or k distinct characters (after shrinking). Since k is a constraint, the space used by the map is proportional to k. In the worst case, k could be s.length, but the problem constraints specify 0 <= k <= 50, which also implies k is bounded by the alphabet size if s contains only ASCII characters.
    • Accuracy: The stated space complexity O(k) is accurate.

3. Key Insight

The key insight for this problem, common to many "longest/shortest substring/subarray" problems, is the two-pointer sliding window approach with a frequency map.

  1. Expansion: We expand the window from the right (right pointer) as long as the current window remains valid (i.e., contains at most k distinct characters). We use a frequency map (characterCount) to keep track of character counts and thus the number of distinct characters (characterCount.size).
  2. Contraction: If adding a character at the right pointer violates the constraint (i.e., characterCount.size > k), we must shrink the window from the left (left pointer). We do this by decrementing the count of the character at s[left] in our frequency map. If its count drops to zero, it means that character is no longer present in the window, so we remove it from the map, effectively reducing the count of distinct characters. We continue shrinking until the window becomes valid again.
  3. Maximization: After each expansion (and subsequent contraction if necessary) that results in a valid window, we update our maxLength with the current window's length (right - left + 1). This ensures we capture the longest valid substring found so far.

This "expand-then-contract" strategy guarantees that at every step, we are maintaining the longest possible valid window ending at the current right pointer.

4. Edge Case Analysis

The solution handles several important edge cases correctly:

  • k = 0: The code explicitly checks if (k === 0) return 0;. This is correct, as a substring with 0 distinct characters is not possible (unless the string itself is empty, but constraints say s.length >= 1).
  • s.length = 1:
    • s = "a", k = 1: right moves to 0. char = 'a'. characterCount = {'a': 1}. characterCount.size (1) is not > k (1). maxLength = max(0, 0 - 0 + 1) = 1. Returns 1. Correct.
  • k is large (e.g., k >= s.length or k >= alphabet_size): The while (characterCount.size > k) condition will rarely or never be true. The maxLength will simply become s.length, which is correct as the entire string itself is a valid substring.
  • All characters are the same:
    • s = "aaaaa", k = 1: characterCount will always have size 1. The while loop won't execute. maxLength will correctly become s.length (5).
  • All characters are distinct:
    • s = "abcde", k = 2:
      • right = 0, char = 'a': {'a':1}, size=1. maxLength=1.
      • right = 1, char = 'b': {'a':1, 'b':1}, size=2. maxLength=2.
      • right = 2, char = 'c': {'a':1, 'b':1, 'c':1}, size=3. size > k.
        • left = 0, leftChar = 'a': {'a':0, 'b':1, 'c':1}. Delete 'a'. left=1. characterCount = {'b':1, 'c':1}, size=2. Valid.
        • maxLength = max(2, 2 - 1 + 1) = 2.
          This logic continues correctly, demonstrating the shrinking mechanism.

The current solution correctly handles these cases.

5. Learning Points

  • Similar Problems Using This Pattern:

  • Common Mistakes with This Pattern:

    • Incorrect maxLength update: Updating maxLength inside the while loop (during contraction) or before the window is guaranteed to be valid can lead to incorrect results. It should be updated after the while loop, when the window is valid and has been expanded as much as possible to the right.
    • Forgetting to remove characters from the map: If a character's count drops to zero, it must be deleted from the Map to accurately reflect the number of distinct characters (characterCount.size).
    • Off-by-one errors in window length: The length of the current window is right - left + 1. A common mistake is using right - left.
    • Incorrect left pointer movement: The left pointer should only move when the window constraint is violated.
  • Variations of This Problem:

    • Minimum Length Substring with At Least/Exactly K Distinct Characters: Instead of maximum length, find minimum length.
    • Count Substrings/Subarrays with At Most/Exactly K Distinct Characters: Instead of length, count the number of valid substrings/subarrays.
    • Problems with different constraints: Instead of distinct characters, the constraint could be sum, product, average, or other properties of elements within the window.

6. Code Quality

The code quality is excellent:

  • Variable Naming: characterCount, left, right, maxLength, char, leftChar are all clear, descriptive, and follow standard conventions.
  • Code Structure and Readability:
    • The comments explaining the time and space complexity are well-placed and accurate.
    • The comments within the loop (// Expand the window..., // Shrink the window..., // Update the maximum length...) clearly delineate the steps of the sliding window algorithm, making it very easy to understand.
    • The early exit if (k === 0) return 0; is a good practice for handling trivial cases.
    • The use of Map<string, number> is appropriate for tracking character frequencies.
    • The non-null assertion operator ! on characterCount.get(leftChar)! is acceptable in competitive programming where the logic guarantees the key exists at that point. For extremely robust production code, a null check or 0 default could be preferred, but it's not a major issue here.
  • TypeScript Best Practices:
    • Using const for characterCount and char (implicitly) is good. let is correctly used for left, maxLength, right (in the loop), and leftChar because they are reassigned.
    • Type annotations (string, number, Map<string, number>) are used correctly.
    • The export keyword was added, which is good for modularity in a larger project.

7. Alternative Approaches

  1. Brute Force (O(N^3) or O(N^2 * AlphabetSize)):

    • Iterate through all possible starting positions i from 0 to n-1.
    • For each i, iterate through all possible ending positions j from i to n-1. This defines a substring s[i...j].
    • For each substring, iterate through its characters to count the number of distinct characters using a Set or a frequency array.
    • If the count is <= k, update the maximum length.
    • Trade-offs: Simple to understand but highly inefficient for larger strings due to nested loops and repeated distinct character counting.
  2. Optimized Brute Force (O(N^2 * k)):

    • Iterate through all possible starting positions i.
    • For each i, iterate through ending positions j from i to n-1.
    • Maintain a frequency map for the current substring s[i...j] as j expands. Update the map with s[j] and check map.size.
    • Trade-offs: Better than the pure brute force by avoiding re-counting distinct characters for each substring from scratch, but still O(N^2) because of the outer loops. The k factor comes from Map operations in the worst case if character set is large, or O(1) average. Still too slow for N = 5 * 10^4.

The current Sliding Window approach is the most optimal solution for this problem due to its linear time complexity.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new LeetCode medium problem solution: "Longest Substring with At Most K Distinct Characters" (Problem #0340). The solution implements the sliding window pattern with a frequency map to efficiently find the longest substring containing at most k distinct characters.

Key changes:

  • Added TypeScript solution with O(n) time complexity using sliding window technique
  • Improved README formatting from HTML to clean Markdown
  • Added JSDoc comments with detailed complexity analysis

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

File Description
leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/longest-substring-with-at-most-k-distinct-characters.ts Implements sliding window solution with export statement, JSDoc comments, and inline documentation
leetcode/medium/0340-longest-substring-with-at-most-k-distinct-characters/README.md Cleaned up problem description from HTML to Markdown format with properly formatted examples and constraints
README.md Added problem link to the Sliding Window pattern section
0340-longest-substring-with-at-most-k-distinct-characters/* Removed files from incorrect root location (files moved to proper leetcode/medium/ directory)

@pertrai1 pertrai1 merged commit 32250c5 into main Dec 30, 2025
15 of 16 checks passed
@pertrai1 pertrai1 deleted the leetcode-12302025 branch December 30, 2025 13:40
@github-actions
Copy link

📅 Spaced Repetition Reviews Scheduled!

Great job solving #0340 - Longest Substring With At Most K Distinct Characters! 🎉

To help you retain this knowledge long-term, I've scheduled 5 review sessions using spaced repetition:

Review Interval Schedule Logic
1st Review 1 day after solving Scheduled now
2nd Review 3 days after 1st review Auto-scheduled when 1st completes
3rd Review 7 days after 2nd review Auto-scheduled when 2nd completes
4th Review 14 days after 3rd review Auto-scheduled when 3rd completes
5th Review 30 days after 4th review Auto-scheduled when 4th completes

What to expect:

  • Your 1st review is scheduled for tomorrow
  • Each subsequent review is scheduled automatically when you complete the previous one
  • This ensures proper spacing even if you complete a review a few days late
  • GitHub issues will be created automatically for each review
  • Each issue will link back to your solution

🧠 Why Spaced Repetition?

Research shows that reviewing material at increasing intervals dramatically improves retention. This adaptive scheduling ensures optimal spacing based on when you actually complete each review!

Check docs/reviews/review-schedule.json to see your review schedule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code challenge this is a coding challenge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants