1624. Largest Substring Between Two Equal Characters
Problem Description
You are given a string s
and need to find the length of the longest substring that lies between two equal characters, where the substring does not include those two equal characters themselves.
For example, if you have the string "abca"
, the character 'a'
appears at positions 0 and 3. The substring between these two 'a'
characters is "bc"
, which has length 2.
The task requires you to:
- Find any two positions in the string where the same character appears
- Calculate the length of the substring between these two occurrences (excluding the characters themselves)
- Return the maximum such length among all possible pairs of equal characters
- If no character appears more than once in the string, return
-1
The solution uses a dictionary to track the first occurrence of each character. When the same character is encountered again, it calculates the distance between the current position and the first occurrence (minus 1 to exclude the characters themselves), keeping track of the maximum distance found.
For instance:
- Input:
"aa"
→ Output:0
(nothing between the two 'a's) - Input:
"abca"
→ Output:2
(substring "bc" between the two 'a's) - Input:
"cbzxy"
→ Output:-1
(no repeated characters)
Intuition
The key insight is that to find the longest substring between two equal characters, we need to maximize the distance between any two occurrences of the same character.
Think about it this way: if a character appears multiple times in the string, the longest possible substring between two instances of that character would be between its first and last occurrences. Any other pair of positions would give us a shorter or equal length substring.
This leads us to a simple strategy:
- We only need to remember the first position where each character appears
- When we encounter a character we've seen before, we calculate the distance from its first occurrence to the current position
- The substring length between them is
current_position - first_position - 1
(we subtract 1 to exclude both characters)
Why does this work? Because as we traverse the string from left to right:
- The first time we see a character, we store its position
- Every subsequent time we see that same character, we're automatically finding a potentially longer substring (since the current position is further from the stored first position)
- We keep updating our answer with the maximum length found so far
For example, if character 'a'
appears at positions 0, 3, and 7:
- When we see it at position 3: substring length =
3 - 0 - 1 = 2
- When we see it at position 7: substring length =
7 - 0 - 1 = 6
- The second calculation automatically gives us the longer substring
This approach is efficient because we only need one pass through the string and constant time lookups in our dictionary.
Solution Approach
The solution uses a hash map (dictionary) to track the first occurrence of each character and a single pass through the string to find the maximum distance.
Implementation Steps:
-
Initialize data structures:
- Create an empty dictionary
d
to store the first occurrence index of each character - Set
ans = -1
as the default return value (for when no repeated characters exist)
- Create an empty dictionary
-
Traverse the string with enumeration:
for i, c in enumerate(s):
This gives us both the index
i
and characterc
at each position. -
Check if character has been seen before:
- If
c in d
: The character has appeared before- Calculate the substring length:
i - d[c] - 1
i
is the current positiond[c]
is the first occurrence position- Subtract 1 to exclude both characters
- Update
ans
with the maximum:ans = max(ans, i - d[c] - 1)
- Calculate the substring length:
- If
c not in d
: This is the first occurrence- Store the current position:
d[c] = i
- This position will be used for all future occurrences of this character
- Store the current position:
- If
-
Return the result:
- After processing all characters,
ans
contains the maximum length found - If no character appeared twice,
ans
remains-1
- After processing all characters,
Example Walkthrough with s = "abca"
:
- i=0, c='a': Not in d, store
d['a'] = 0
- i=1, c='b': Not in d, store
d['b'] = 1
- i=2, c='c': Not in d, store
d['c'] = 2
- i=3, c='a': Found in d! Calculate
3 - 0 - 1 = 2
, updateans = 2
- Return
2
Time Complexity: O(n)
where n is the length of the string (single pass)
Space Complexity: O(min(n, 26))
for the dictionary (at most 26 lowercase letters or n unique characters)
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 = "cabbac"
.
Initial Setup:
d = {}
(empty dictionary to store first occurrences)ans = -1
(default return value)
Step-by-step traversal:
i=0, c='c':
- 'c' is not in dictionary
- Store first occurrence:
d['c'] = 0
- Dictionary:
{'c': 0}
ans = -1
i=1, c='a':
- 'a' is not in dictionary
- Store first occurrence:
d['a'] = 1
- Dictionary:
{'c': 0, 'a': 1}
ans = -1
i=2, c='b':
- 'b' is not in dictionary
- Store first occurrence:
d['b'] = 2
- Dictionary:
{'c': 0, 'a': 1, 'b': 2}
ans = -1
i=3, c='b':
- 'b' is in dictionary at position 2!
- Calculate substring length:
3 - 2 - 1 = 0
- The substring between the two 'b's is empty (they're adjacent)
- Update:
ans = max(-1, 0) = 0
i=4, c='a':
- 'a' is in dictionary at position 1!
- Calculate substring length:
4 - 1 - 1 = 2
- The substring between positions 1 and 4 is "bb" (length 2)
- Update:
ans = max(0, 2) = 2
i=5, c='c':
- 'c' is in dictionary at position 0!
- Calculate substring length:
5 - 0 - 1 = 4
- The substring between positions 0 and 5 is "abba" (length 4)
- Update:
ans = max(2, 4) = 4
Final Result: Return 4
The longest substring between two equal characters is "abba" (between the two 'c' characters at positions 0 and 5).
Solution Implementation
1class Solution:
2 def maxLengthBetweenEqualCharacters(self, s: str) -> int:
3 # Dictionary to store the first occurrence index of each character
4 first_occurrence = {}
5
6 # Initialize the maximum length to -1 (default when no equal characters found)
7 max_length = -1
8
9 # Iterate through the string with index and character
10 for index, char in enumerate(s):
11 if char in first_occurrence:
12 # If character was seen before, calculate the length between occurrences
13 # Length = current_index - first_index - 1 (excluding the characters themselves)
14 current_length = index - first_occurrence[char] - 1
15 max_length = max(max_length, current_length)
16 else:
17 # Store the first occurrence index of this character
18 first_occurrence[char] = index
19
20 return max_length
21
1class Solution {
2 public int maxLengthBetweenEqualCharacters(String s) {
3 // Array to store the first occurrence index of each character (a-z)
4 // Initialize with -1 to indicate character hasn't been seen yet
5 int[] firstOccurrence = new int[26];
6 Arrays.fill(firstOccurrence, -1);
7
8 // Variable to track the maximum length between equal characters
9 int maxLength = -1;
10
11 // Iterate through each character in the string
12 for (int currentIndex = 0; currentIndex < s.length(); ++currentIndex) {
13 // Convert character to index (0-25) by subtracting 'a'
14 int charIndex = s.charAt(currentIndex) - 'a';
15
16 // Check if this is the first occurrence of this character
17 if (firstOccurrence[charIndex] == -1) {
18 // Store the first occurrence index
19 firstOccurrence[charIndex] = currentIndex;
20 } else {
21 // Calculate length between first and current occurrence (excluding both characters)
22 // Update maxLength if current distance is greater
23 maxLength = Math.max(maxLength, currentIndex - firstOccurrence[charIndex] - 1);
24 }
25 }
26
27 // Return the maximum length found, or -1 if no equal characters exist
28 return maxLength;
29 }
30}
31
1class Solution {
2public:
3 int maxLengthBetweenEqualCharacters(string s) {
4 // Array to store the first occurrence index of each character (a-z)
5 // Initialize with -1 to indicate character hasn't been seen yet
6 vector<int> firstOccurrence(26, -1);
7
8 // Variable to track the maximum length between equal characters
9 int maxLength = -1;
10
11 // Iterate through each character in the string
12 for (int currentIndex = 0; currentIndex < s.size(); ++currentIndex) {
13 // Convert character to index (0-25 for a-z)
14 int charIndex = s[currentIndex] - 'a';
15
16 // Check if this is the first occurrence of the character
17 if (firstOccurrence[charIndex] == -1) {
18 // Record the first occurrence position
19 firstOccurrence[charIndex] = currentIndex;
20 } else {
21 // Character seen before, calculate length between occurrences
22 // Length = current position - first position - 1 (excluding the characters themselves)
23 int lengthBetween = currentIndex - firstOccurrence[charIndex] - 1;
24 maxLength = max(maxLength, lengthBetween);
25 }
26 }
27
28 // Return the maximum length found, or -1 if no equal characters exist
29 return maxLength;
30 }
31};
32
1/**
2 * Finds the maximum length of substring between two equal characters
3 * @param s - The input string to search
4 * @returns The maximum length between equal characters, or -1 if no equal characters exist
5 */
6function maxLengthBetweenEqualCharacters(s: string): number {
7 const stringLength: number = s.length;
8
9 // Array to store the first occurrence index of each lowercase letter (a-z)
10 // Index represents the character (0 for 'a', 1 for 'b', etc.)
11 const firstOccurrenceIndex: number[] = new Array(26).fill(-1);
12
13 // Track the maximum length found between equal characters
14 let maxLength: number = -1;
15
16 // Iterate through each character in the string
17 for (let currentIndex = 0; currentIndex < stringLength; currentIndex++) {
18 // Convert character to its corresponding array index (0-25)
19 const charIndex: number = s[currentIndex].charCodeAt(0) - 'a'.charCodeAt(0);
20
21 if (firstOccurrenceIndex[charIndex] === -1) {
22 // First occurrence of this character - store its position
23 firstOccurrenceIndex[charIndex] = currentIndex;
24 } else {
25 // Character seen before - calculate distance between occurrences
26 // Subtract 1 to exclude the characters themselves
27 const lengthBetween: number = currentIndex - firstOccurrenceIndex[charIndex] - 1;
28 maxLength = Math.max(maxLength, lengthBetween);
29 }
30 }
31
32 return maxLength;
33}
34
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the string s
. The algorithm iterates through the string exactly once using a single for loop. Each operation inside the loop (checking if a character exists in the dictionary, updating the dictionary, and calculating the maximum) takes O(1)
time on average for dictionary operations.
Space Complexity: O(min(n, 26))
which simplifies to O(1)
for practical purposes since we're dealing with lowercase English letters. The dictionary d
stores at most one entry per unique character in the string. In the worst case where all characters are unique, the dictionary would store n
entries, but since we're typically dealing with English alphabet characters, the maximum number of entries is bounded by 26 (or 52 if considering both cases, or 128/256 for extended ASCII). Therefore, the space complexity is O(1)
constant space when considering the character set is fixed.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Updating the First Occurrence Position
A common mistake is updating the dictionary for every occurrence of a character, not just the first one:
Incorrect approach:
for index, char in enumerate(s):
if char in first_occurrence:
current_length = index - first_occurrence[char] - 1
max_length = max(max_length, current_length)
first_occurrence[char] = index # Wrong: This updates for every occurrence!
Why this is wrong: By updating the position on every occurrence, you lose track of the first position, which gives you the maximum possible distance. For example, with "abcba"
, the two 'b's are at positions 1 and 3, giving length 1. But if you update the position, you might miss the maximum distance between the two 'a's at positions 0 and 4, which gives length 3.
Correct approach:
for index, char in enumerate(s):
if char in first_occurrence:
current_length = index - first_occurrence[char] - 1
max_length = max(max_length, current_length)
else:
first_occurrence[char] = index # Only store the FIRST occurrence
2. Forgetting to Subtract 1
Another pitfall is calculating the distance incorrectly by forgetting to exclude the characters themselves:
Incorrect calculation:
current_length = index - first_occurrence[char] # Wrong: includes both characters
Why this is wrong: The problem asks for the substring between the two equal characters, excluding them. If you don't subtract 1, you're counting one of the boundary characters.
Example: For "aa"
at positions 0 and 1:
- Wrong:
1 - 0 = 1
(incorrectly suggests there's 1 character between them) - Correct:
1 - 0 - 1 = 0
(correctly shows nothing between them)
3. Not Handling the No-Repeat Case
Failing to initialize the return value to -1:
Incorrect initialization:
max_length = 0 # Wrong: assumes at least one pair exists
Why this is wrong: If no character repeats (like in "abcd"
), the function should return -1, not 0. Starting with 0 would incorrectly suggest there's a valid substring of length 0.
Correct initialization:
max_length = -1 # Correct: handles the case when no character repeats
In a binary min heap, the maximum element can be found in:
Recommended Readings
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!