Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Build confidence with an intuition-first walkthrough focused on hash map fundamentals.
s, return the maximum length of a substring such that it contains at most two occurrences of each character.
Example 1:
Input: s = "bcbbbcba"
Output: 4
Explanation:
The following substring has a length of 4 and contains at most two occurrences of each character:"bcbbbcba".Example 2:
Input: s = "aaaa"
Output: 2
Explanation:
The following substring has a length of 2 and contains at most two occurrences of each character:"aaaa".Constraints:
2 <= s.length <= 100s consists only of lowercase English letters.Problem summary: Given a string s, return the maximum length of a substring such that it contains at most two occurrences of each character.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Sliding Window
"bcbbbcba"
"aaaa"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3090: Maximum Length Substring With Two Occurrences
class Solution {
public int maximumLengthSubstring(String s) {
int[] cnt = new int[26];
int ans = 0;
for (int i = 0, j = 0; j < s.length(); ++j) {
int idx = s.charAt(j) - 'a';
++cnt[idx];
while (cnt[idx] > 2) {
--cnt[s.charAt(i++) - 'a'];
}
ans = Math.max(ans, j - i + 1);
}
return ans;
}
}
// Accepted solution for LeetCode #3090: Maximum Length Substring With Two Occurrences
func maximumLengthSubstring(s string) (ans int) {
cnt := [26]int{}
i := 0
for j, c := range s {
idx := c - 'a'
cnt[idx]++
for cnt[idx] > 2 {
cnt[s[i]-'a']--
i++
}
ans = max(ans, j-i+1)
}
return
}
# Accepted solution for LeetCode #3090: Maximum Length Substring With Two Occurrences
class Solution:
def maximumLengthSubstring(self, s: str) -> int:
cnt = Counter()
ans = i = 0
for j, c in enumerate(s):
cnt[c] += 1
while cnt[c] > 2:
cnt[s[i]] -= 1
i += 1
ans = max(ans, j - i + 1)
return ans
// Accepted solution for LeetCode #3090: Maximum Length Substring With Two Occurrences
fn maximum_length_substring(s: String) -> i32 {
let cs: Vec<_> = s.bytes().collect();
let mut left = 0;
let mut right = 0;
let mut ret = 0;
let mut table = vec![0; 26];
while left < s.len() && right < s.len() {
let idx = (cs[right] - b'a') as usize;
table[idx] += 1;
while table[idx] > 2 && left < s.len() {
let idx_left = (cs[left] - b'a') as usize;
table[idx_left] -= 1;
left += 1;
}
ret = std::cmp::max(ret, right - left + 1);
right += 1;
}
ret as i32
}
fn main() {
let ret = maximum_length_substring("bcbbbcba".to_string());
println!("ret={ret}");
}
#[test]
fn test() {
assert_eq!(maximum_length_substring("bcbbbcba".to_string()), 4);
assert_eq!(maximum_length_substring("aaaa".to_string()), 2);
}
// Accepted solution for LeetCode #3090: Maximum Length Substring With Two Occurrences
function maximumLengthSubstring(s: string): number {
let ans = 0;
const cnt: number[] = Array(26).fill(0);
for (let i = 0, j = 0; j < s.length; ++j) {
const idx = s[j].charCodeAt(0) - 'a'.charCodeAt(0);
++cnt[idx];
while (cnt[idx] > 2) {
--cnt[s[i++].charCodeAt(0) - 'a'.charCodeAt(0)];
}
ans = Math.max(ans, j - i + 1);
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
For each starting index, scan the next k elements to compute the window aggregate. There are n−k+1 starting positions, each requiring O(k) work, giving O(n × k) total. No extra space since we recompute from scratch each time.
The window expands and contracts as we scan left to right. Each element enters the window at most once and leaves at most once, giving 2n total operations = O(n). Space depends on what we track inside the window (a hash map of at most k distinct elements, or O(1) for a fixed-size window).
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Using `if` instead of `while` leaves the window invalid for multiple iterations.
Usually fails on: Over-limit windows stay invalid and produce wrong lengths/counts.
Fix: Shrink in a `while` loop until the invariant is valid again.