Editorial
Core insight
Find Longest Special Substring That Occurs Thrice I · Sliding Window
Core Insight for Find Longest Special Substring That Occurs Thrice I
The key intuition is that we do not need to check arbitrary substrings like index 2 to 5. We only care about contiguous blocks of identical characters.
If we find a block like "aaaa" (length 4), this single block implicitly contains:
- One special substring of length 4 (
"aaaa") - Two special substrings of length 3 (
"aaa") - Three special substrings of length 2 (
"aa") - Four special substrings of length 1 (
"a")
Invariant:
The sliding window [left, right) always maintains a range of identical characters.
Visual Description:
Imagine the window starting at the first character. The right pointer expands as long as s[right] matches s[left]. When s[right] differs, the window [left, right) represents a maximal special substring. We process this block to update our counts, then the left pointer jumps to right to begin a new window. This ensures we process the string in a single linear pass (plus a small inner loop for counting), drastically reducing complexity compared to brute force.
Deep dive
Full breakdown
Full reasoning, pitfalls, and implementation flow
LeetCode 2981: Find Longest Special Substring That Occurs Thrice I Solution & Explanation
Problem Overview
TL;DR: Use a variable-size sliding window to group contiguous identical characters, calculate the frequency of all special substrings derived from these groups, and return the maximum length occurring at least three times.
In LeetCode 2981, "Find Longest Special Substring That Occurs Thrice I", you are given a string s. You must identify the length of the longest substring that meets two criteria:
- It is special (composed of a single unique character, e.g., "aaa").
- It appears at least three times in the original string.
If no such substring exists, return -1. While the constraints for this specific version (I) are small (), the logic required is foundational for handling larger string constraints efficiently.
Brute Force Approach for Find Longest Special Substring That Occurs Thrice I
The naive approach involves generating every possible substring, checking if it is "special," and then counting its total occurrences.
- Generate all substrings of
susing nested loops. - For each substring, iterate through its characters to verify if they are all identical.
- If special, store it in a frequency map (Hash Map).
- Finally, iterate through the map to find the longest key with a value .
Pseudo-code:
max_len = -1
for i from 0 to n:
for j from i to n:
sub = s[i...j]
if isSpecial(sub):
map[sub]++
for sub, count in map:
if count >= 3:
max_len = max(max_len, sub.length)Time Complexity: . Generating substrings is , and verifying/hashing each can take . Why it fails: While allows this to pass, this approach scales poorly. For larger constraints (like version II of this problem), results in Time Limit Exceeded (TLE). It performs redundant checks on parts of the string that have already been processed.
Algorithm Strategy: Sliding Window Patterns
- State Management: maintain a 2D array or a map
countswherecounts[char][length]stores the frequency of a special substring of a specificlengthmade ofchar. - Window Initialization: Initialize
leftat 0. - Expansion: Iterate
rightfrom 0 ton. - Condition Check:
- We treat the window as a contiguous block of identical characters.
- We detect the end of a block when
right == n(end of string) ors[right] != s[left].
- Processing:
- When a block ends, calculate its length
L = right - left. - The character is
c = s[left]. - This block contributes to the counts of all special substrings of length to . Specifically, a block of length contains occurrences of a special substring of length .
- Update the
countsdata structure accordingly.
- When a block ends, calculate its length
- Contraction/Reset: Move
lefttorightto start measuring the next block of characters. - Result: After the loop, scan the
countsstructure. Return the maximum length such that any character hascounts[char][k] >= 3.
Execution Flow
Let's trace s = "aaaba":
- Initialize:
left = 0,countsmap empty. - Iteration
right = 0:s[0]is 'a'. Continue. - Iteration
right = 1:s[1]is 'a' (matchess[left]). Continue. - Iteration
right = 2:s[2]is 'a' (matchess[left]). Continue. - Iteration
right = 3:s[3]is 'b' (mismatch!).- Process Window:
[0, 3)is"aaa". Length . Char 'a'. - Update counts for 'a':
- Len 1: Add . Total 'a' len 1 count: 3.
- Len 2: Add . Total 'a' len 2 count: 2.
- Len 3: Add . Total 'a' len 3 count: 1.
- Reset: Set
left = 3.
- Process Window:
- Iteration
right = 4:s[4]is 'a' (mismatch withs[left]which is 'b').- Process Window:
[3, 4)is"b". Length . Char 'b'. - Update counts for 'b':
- Len 1: Add . Total 'b' len 1 count: 1.
- Reset: Set
left = 4.
- Process Window:
- End of String: Loop finishes, but we have a remaining segment starting at
left=4.- Process Window:
[4, 5)is"a". Length . Char 'a'. - Update counts for 'a':
- Len 1: Add 1. Total 'a' len 1 count: .
- Process Window:
- Final Check:
- 'a', len 1: count 4 (). Max result = 1.
- 'a', len 2: count 2 ().
- 'b', len 1: count 1 ().
- Output: 1.
Proof of Correctness
The algorithm correctly identifies every maximal contiguous segment of identical characters. Since any "special" substring must be part of such a segment, processing these segments covers all possibilities. The formula mathematically accounts for every valid position a substring of length could occupy within a block of length . By aggregating these counts globally, we ensure that split occurrences (e.g., "aa...aa") are summed correctly.
Pattern Reuse Notes
The Sliding Window - Variable Size pattern is versatile and appears in many popular interview questions:
- LeetCode 3: Longest Substring Without Repeating Characters - Uses a similar expansion/contraction logic but tracks character uniqueness instead of homogeneity.
- LeetCode 76: Minimum Window Substring - Expands to satisfy frequency counts, then shrinks to minimize length.
- LeetCode 209: Minimum Size Subarray Sum - Expands to meet a sum threshold, then shrinks to find the minimal size.
- LeetCode 219: Contains Duplicate II - Maintains a window of fixed size (or variable constrained by distance) to find duplicates.
Feynman coach · Before you peek at the code
Can you explain the core insight in your own words?
If you can explain it before reading code, you’re far more likely to recall it under interview pressure.
Reference Implementation
C++ Solution for LeetCode 2981
1#include <vector>
2#include <string>
3#include <algorithm>
4#include <map>
5
6using namespace std;
7
8class Solution {
9public:
10 int maximumLength(string s) {
11 int n = s.length();
12 // count[char_index][length] stores frequency
13 vector<vector<int>> count(26, vector<int>(n + 1, 0));
14
15 int left = 0;
16
17 for (int right = 0; right < n; ++right) {
18 // Check if window breaks (next char differs) or we are at the end
19 if (right == n - 1 || s[right] != s[right + 1]) {
20 int len = right - left + 1;
21 int charIdx = s[left] - 'a';
22
23 // A block of length 'len' contributes to all sub-lengths
24 for (int k = 1; k <= len; ++k) {
25 count[charIdx][k] += (len - k + 1);
26 }
27
28 // Move left to the start of the next block
29 left = right + 1;
30 }
31 }
32
33 int maxLen = -1;
34 // Check all characters and lengths
35 for (int i = 0; i < 26; ++i) {
36 for (int len = n; len >= 1; --len) {
37 if (count[i][len] >= 3) {
38 maxLen = max(maxLen, len);
39 break; // Optimization: found longest for this char
40 }
41 }
42 }
43
44 return maxLen;
45 }
46};Java Solution for LeetCode 2981
1class Solution {
2 public int maximumLength(String s) {
3 int n = s.length();
4 // count[char_index][length]
5 int[][] count = new int[26][n + 1];
6
7 int left = 0;
8
9 for (int right = 0; right < n; right++) {
10 // Detect end of a contiguous block
11 if (right == n - 1 || s.charAt(right) != s.charAt(right + 1)) {
12 int len = right - left + 1;
13 int charIdx = s.charAt(left) - 'a';
14
15 // Update frequencies for all valid sub-lengths within this block
16 for (int k = 1; k <= len; k++) {
17 count[charIdx][k] += (len - k + 1);
18 }
19
20 // Reset window start
21 left = right + 1;
22 }
23 }
24
25 int maxLen = -1;
26 for (int i = 0; i < 26; i++) {
27 for (int len = n; len >= 1; len--) {
28 if (count[i][len] >= 3) {
29 maxLen = Math.max(maxLen, len);
30 break; // Found max for this char
31 }
32 }
33 }
34
35 return maxLen;
36 }
37}Python Solution for LeetCode 2981
1class Solution:
2 def maximumLength(self, s: str) -> int:
3 n = len(s)
4 # Dictionary to map (char, length) -> frequency
5 counts = {}
6
7 left = 0
8 for right in range(n):
9 # If current char is last char or next char is different
10 if right == n - 1 or s[right] != s[right + 1]:
11 length = right - left + 1
12 char = s[left]
13
14 # Update counts for all possible sub-lengths
15 for k in range(1, length + 1):
16 key = (char, k)
17 # Formula: a block of length L has (L - k + 1) substrings of length k
18 counts[key] = counts.get(key, 0) + (length - k + 1)
19
20 left = right + 1
21
22 max_len = -1
23 for (char, length), count in counts.items():
24 if count >= 3:
25 max_len = max(max_len, length)
26
27 return max_len