Intuition¶
We want the longest substring where no character appears more than twice. Validity is monotonic: if a window is valid, so is any window inside it. This is the classic sliding-window setup — grow the right edge, and shrink from the left only when a character's count exceeds two.
Approach: Sliding Window + Frequency Counts¶
Keep a window [left, right] and a size-26 frequency array for lowercase letters.
- Extend
right, incrementing the count of the new character. - If that character now appears more than twice, shrink from the left —
decrementing counts and advancing
left— until it appears at most twice again. Only the just-added character can break the invariant, so checking it suffices. - After each step the window is valid; update the answer with its length
right - left + 1.
Each index enters and leaves the window at most once, so the scan is linear.
Complexity¶
- Time complexity: $$O(n)$$, where
nis the length ofs— each character is added and removed at most once. - Space complexity: $$O(1)$$ — a fixed 26-element frequency array.
Code¶
Go¶
func maximumLengthSubstring(s string) int {
freq := [26]int{}
left, ans := 0, 0
for right, ch := range s {
index := ch - 'a'
freq[index]++
for freq[index] > 2 {
leftIdx := s[left] - 'a'
freq[leftIdx]--
left++
}
ans = max(ans, right - left + 1)
}
return ans
}
Rust¶
impl Solution {
pub fn maximum_length_substring(s: String) -> i32 {
let s = s.as_bytes();
let mut freq = [0; 26];
let (mut left, mut ans) = (0, 0);
for right in 0..s.len() {
let right_idx = (s[right] - b'a') as usize;
freq[right_idx] += 1;
while freq[right_idx] > 2 {
let left_idx = (s[left] - b'a') as usize;
freq[left_idx] -= 1;
left += 1;
}
ans = ans.max(right - left + 1);
}
ans as i32
}
}