2982. Find Longest Special Substring That Occurs Thrice II
Problem Description
You are given a string s consisting of lowercase English letters.
A special string is defined as a string made up of only a single character. For example:
"abc"is not special (contains different characters)"ddd"is special (all characters are 'd')"zz"is special (all characters are 'z')"f"is special (single character)
Your task is to find the length of the longest special substring of s that occurs at least three times in the string.
A substring is a contiguous non-empty sequence of characters within a string.
Return:
- The length of the longest special substring that appears at least 3 times
-1if no such special substring exists
For example, if you have a string where the substring "aa" appears 3 or more times in different positions, and this is the longest such special substring, you would return 2.
Intuition
The key observation is that if a special substring of length x appears at least 3 times, then a special substring of length x-1 must also appear at least 3 times. This is because we can simply trim one character from each occurrence of the length x substring to get occurrences of length x-1.
For example, if "aaa" appears 3 times in the string, then "aa" must appear at least 3 times (actually more, since each "aaa" contains two "aa" substrings).
This monotonic property creates a pattern like: true, true, true, ..., false, false as we increase the length. We want to find the last true position (maximum valid length). Using our standard binary search template that finds the first true, we can invert the condition: define feasible(x) as "length x does NOT work" (i.e., no special substring of length x appears at least 3 times). Then the pattern becomes false, false, ..., true, true and we find the first infeasible length, then subtract 1.
For checking if a given length x works, we find all maximal runs of the same character and count substrings efficiently. For each run of length L, we can extract max(0, L - x + 1) special substrings of length x. For instance, from "aaaaa" (length 5), we can extract 3 substrings of length 3 (starting at positions 0, 1, and 2).
Learn more about Binary Search and Sliding Window patterns.
Solution Implementation
1from collections import defaultdict
2
3class Solution:
4 def maximumLength(self, s: str) -> int:
5 """
6 Find the maximum length of a special substring that occurs at least 3 times.
7 A special substring consists of a single repeated character.
8 """
9 string_length = len(s)
10
11 def is_infeasible(target_length: int) -> bool:
12 """
13 Check if target_length is infeasible (no special substring of this
14 length appears at least 3 times).
15 Returns True if infeasible, False if feasible.
16 """
17 if target_length == 0:
18 return False # Length 0 is always feasible (edge case)
19
20 occurrence_count = defaultdict(int)
21
22 # Find consecutive character runs
23 i = 0
24 while i < string_length:
25 j = i + 1
26 while j < string_length and s[j] == s[i]:
27 j += 1
28
29 # Count substrings of target_length from this run
30 group_length = j - i
31 possible_substrings = max(0, group_length - target_length + 1)
32 occurrence_count[s[i]] += possible_substrings
33
34 i = j
35
36 # Return True if infeasible (no character has 3+ occurrences)
37 return max(occurrence_count.values()) < 3
38
39 # Binary search using the template: find first infeasible length
40 left, right = 1, string_length
41 first_true_index = -1
42
43 while left <= right:
44 mid = (left + right) // 2
45 if is_infeasible(mid):
46 first_true_index = mid
47 right = mid - 1
48 else:
49 left = mid + 1
50
51 # Handle edge cases
52 if first_true_index == -1:
53 # All lengths are feasible (shouldn't happen with proper bounds)
54 return string_length
55 if first_true_index == 1:
56 # Even length 1 is infeasible
57 return -1
58
59 # Return the last feasible length
60 return first_true_index - 1
611class Solution {
2 private String inputString;
3 private int stringLength;
4
5 /**
6 * Finds the maximum length of a special substring that appears at least 3 times.
7 * A special substring consists of only a single repeated character.
8 */
9 public int maximumLength(String s) {
10 this.inputString = s;
11 this.stringLength = s.length();
12
13 // Binary search using template: find first infeasible length
14 int left = 1;
15 int right = stringLength;
16 int firstTrueIndex = -1;
17
18 while (left <= right) {
19 int mid = left + (right - left) / 2;
20
21 if (isInfeasible(mid)) {
22 firstTrueIndex = mid;
23 right = mid - 1;
24 } else {
25 left = mid + 1;
26 }
27 }
28
29 // Handle edge cases
30 if (firstTrueIndex == -1) {
31 return stringLength; // All lengths feasible
32 }
33 if (firstTrueIndex == 1) {
34 return -1; // Even length 1 infeasible
35 }
36
37 return firstTrueIndex - 1;
38 }
39
40 /**
41 * Checks if the given length is infeasible (no special substring of this
42 * length appears at least 3 times).
43 * Returns true if infeasible, false if feasible.
44 */
45 private boolean isInfeasible(int targetLength) {
46 int[] occurrenceCount = new int[26];
47
48 for (int i = 0; i < stringLength;) {
49 int j = i + 1;
50
51 while (j < stringLength && inputString.charAt(j) == inputString.charAt(i)) {
52 j++;
53 }
54
55 int charIndex = inputString.charAt(i) - 'a';
56 occurrenceCount[charIndex] += Math.max(0, j - i - targetLength + 1);
57
58 if (occurrenceCount[charIndex] >= 3) {
59 return false; // Feasible - found 3+ occurrences
60 }
61
62 i = j;
63 }
64
65 return true; // Infeasible - no character has 3+ occurrences
66 }
67}
681class Solution {
2public:
3 int maximumLength(string s) {
4 int stringLength = s.size();
5
6 // Lambda: returns true if length is infeasible (no 3+ occurrences)
7 auto isInfeasible = [&](int targetLength) {
8 int occurrenceCount[26]{};
9
10 for (int i = 0; i < stringLength;) {
11 int j = i + 1;
12 while (j < stringLength && s[j] == s[i]) {
13 ++j;
14 }
15
16 int charIndex = s[i] - 'a';
17 int segmentLength = j - i;
18 occurrenceCount[charIndex] += max(0, segmentLength - targetLength + 1);
19
20 if (occurrenceCount[charIndex] >= 3) {
21 return false; // Feasible
22 }
23
24 i = j;
25 }
26 return true; // Infeasible
27 };
28
29 // Binary search using template: find first infeasible length
30 int left = 1, right = stringLength;
31 int firstTrueIndex = -1;
32
33 while (left <= right) {
34 int mid = left + (right - left) / 2;
35
36 if (isInfeasible(mid)) {
37 firstTrueIndex = mid;
38 right = mid - 1;
39 } else {
40 left = mid + 1;
41 }
42 }
43
44 // Handle edge cases
45 if (firstTrueIndex == -1) {
46 return stringLength;
47 }
48 if (firstTrueIndex == 1) {
49 return -1;
50 }
51
52 return firstTrueIndex - 1;
53 }
54};
551/**
2 * Finds the maximum length of a substring that appears at least 3 times in the string
3 * where the substring consists of only one unique character
4 */
5function maximumLength(s: string): number {
6 const stringLength: number = s.length;
7
8 /**
9 * Returns true if targetLength is infeasible (no 3+ occurrences)
10 */
11 const isInfeasible = (targetLength: number): boolean => {
12 const characterCounts: number[] = Array(26).fill(0);
13
14 let currentIndex: number = 0;
15 while (currentIndex < stringLength) {
16 let nextIndex: number = currentIndex + 1;
17 while (nextIndex < stringLength && s[nextIndex] === s[currentIndex]) {
18 nextIndex++;
19 }
20
21 const characterIndex: number = s[currentIndex].charCodeAt(0) - 'a'.charCodeAt(0);
22 const segmentLength: number = nextIndex - currentIndex;
23 const possibleSubstrings: number = Math.max(0, segmentLength - targetLength + 1);
24 characterCounts[characterIndex] += possibleSubstrings;
25
26 if (characterCounts[characterIndex] >= 3) {
27 return false; // Feasible
28 }
29
30 currentIndex = nextIndex;
31 }
32
33 return true; // Infeasible
34 };
35
36 // Binary search using template: find first infeasible length
37 let left: number = 1;
38 let right: number = stringLength;
39 let firstTrueIndex: number = -1;
40
41 while (left <= right) {
42 const mid: number = Math.floor((left + right) / 2);
43
44 if (isInfeasible(mid)) {
45 firstTrueIndex = mid;
46 right = mid - 1;
47 } else {
48 left = mid + 1;
49 }
50 }
51
52 // Handle edge cases
53 if (firstTrueIndex === -1) {
54 return stringLength;
55 }
56 if (firstTrueIndex === 1) {
57 return -1;
58 }
59
60 return firstTrueIndex - 1;
61}
62Solution Approach
The solution uses binary search combined with a sliding window counting technique.
Binary Search Template Setup
We use the standard binary search template to find the first length that does NOT work:
left, right = 1, n first_true_index = -1 while left <= right: mid = (left + right) // 2 if not feasible(mid): # length mid does NOT have 3+ occurrences first_true_index = mid right = mid - 1 else: left = mid + 1 # first_true_index is the first infeasible length # Answer is first_true_index - 1 (last feasible length)
The feasible(x) function returns True if length x is infeasible (no special substring of length x appears at least 3 times). This inverts the natural condition so we can use the standard "find first true" template.
Feasible Function Implementation
The feasible check determines if there exists a special substring of length x appearing at least 3 times:
-
Initialize a counter: Use
defaultdict(int)to count occurrences for each character -
Find maximal runs using two pointers:
i = 0 while i < n: j = i + 1 while j < n and s[j] == s[i]: j += 1 # Process run from i to j-1 i = j -
Count substrings: For each run of length
L = j - i, we can extractmax(0, L - x + 1)special substrings of lengthx -
Return the inverse: Return
Trueif no character has 3+ occurrences (infeasible)
Final Result
After binary search completes:
- If
first_true_index == 1, even length 1 doesn't work, return-1 - Otherwise, return
first_true_index - 1as the maximum valid length
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the string s = "aaaa".
Step 1: Binary Search Initialization
- String length
n = 4 - Binary search range:
left = 1,right = 4 first_true_index = -1(tracks first infeasible length)
Step 2: Binary Search Iterations
First iteration: mid = (1 + 4) / 2 = 2
- Check if length 2 is infeasible (no 3+ occurrences)
- Run
"aaaa"(length 4): contributes4 - 2 + 1 = 3substrings - Count for 'a': 3 ≥ 3 → length 2 is feasible
- infeasible =
False - Update:
left = 3
Second iteration: mid = (3 + 4) / 2 = 3
- Check if length 3 is infeasible
- Run
"aaaa"(length 4): contributes4 - 3 + 1 = 2substrings - Count for 'a': 2 < 3 → length 3 is infeasible
- infeasible =
True - Update:
first_true_index = 3,right = 2
Step 3: Binary Search Termination
left = 3 > right = 2→ loop endsfirst_true_index = 3(first infeasible length)- Return
first_true_index - 1 = 2
The longest special substring that appears at least 3 times is "aa" with length 2.
Another Example: s = "abcaba"
Binary Search Process:
- Initial:
left = 1,right = 6,first_true_index = -1
mid = 3: Check length 3
- All runs have length 1, so
max(0, 1 - 3 + 1) = 0substrings - infeasible =
True - Update:
first_true_index = 3,right = 2
mid = 1: Check length 1
- Each run contributes 1 substring
- Count: 'a' = 3, 'b' = 2, 'c' = 1
- 'a' has 3 ≥ 3 → length 1 is feasible
- infeasible =
False - Update:
left = 2
mid = 2: Check length 2
-
All runs have length 1 < 2, so 0 substrings
-
infeasible =
True -
Update:
first_true_index = 2,right = 1 -
left = 2 > right = 1→ loop ends -
Return
first_true_index - 1 = 1
The special substring "a" appears 3 times.
Time and Space Complexity
Time Complexity: O(n × log n)
The algorithm uses binary search on the answer range [0, n], which takes O(log n) iterations. In each iteration, the check function is called:
- The
checkfunction iterates through the string once with two pointers (iandj), takingO(n)time to identify consecutive character segments - For each segment, it performs constant time operations to update the count in the dictionary
- Finding the maximum value in the dictionary takes
O(|Σ|)time where|Σ| = 26for lowercase letters
Since |Σ| is constant (26), the time per check call is O(n). Therefore, the total time complexity is O(n × log n).
Space Complexity: O(|Σ|) or O(1)
The space is used by:
- The
cntdictionary in thecheckfunction, which stores at most|Σ|entries (one for each distinct character in the string) - Since we're dealing with lowercase English letters,
|Σ| = 26, making thisO(26) = O(1)constant space - A few integer variables (
i,j,l,r,mid) which useO(1)space
Therefore, the space complexity is O(|Σ|) where |Σ| = 26, which simplifies to O(1).
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template
Pitfall: Not using the standard binary search template with first_true_index tracking.
Common mistakes:
# WRONG: Using while left < right with different update rules while left < right: mid = (left + right + 1) >> 1 if check(mid): left = mid else: right = mid - 1
Correct approach using template:
# CORRECT: Standard template with first_true_index left, right = 1, n first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): # Define feasible as "condition becomes TRUE" first_true_index = mid right = mid - 1 else: left = mid + 1
2. Incorrect Counting of Overlapping Substrings
Pitfall: Incorrectly counting how many special substrings of length x can be formed from a consecutive run.
Incorrect approach:
# WRONG: Dividing the group length by target length possible_substrings = group_length // target_length
Why it's wrong: For a run like "aaaa" (length 4) and target length 2:
- Wrong calculation:
4 // 2 = 2 - Correct count: 3 overlapping substrings at positions [0:2], [1:3], [2:4]
Correct approach:
# CORRECT: Using sliding window formula
possible_substrings = max(0, group_length - target_length + 1)
3. Forgetting to Invert the Condition for "Find Maximum"
Pitfall: The template finds the first true position, but we need the last feasible position (maximum length). Must invert the condition.
Wrong:
# WRONG: feasible returns True when length works
def feasible(x):
return max(counts.values()) >= 3 # True = works
Correct:
# CORRECT: feasible returns True when length does NOT work (infeasible)
def is_infeasible(x):
return max(counts.values()) < 3 # True = doesn't work
# Then: first_true_index - 1 = last feasible length
4. Forgetting Edge Cases
Pitfall: Not handling when first_true_index == 1 (even length 1 is infeasible).
Wrong:
return first_true_index - 1 # Returns 0 when should return -1
Correct:
if first_true_index == 1: return -1 # No valid length exists return first_true_index - 1
Which of these properties could exist for a graph but not a tree?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!