3884. First Matching Character From Both Ends
Problem Description
You are given a string s of length n made up of lowercase English letters.
Your task is to find and return the smallest index i such that the character at position i is equal to the character at the mirrored position from the end, that is s[i] == s[n - i - 1].
In other words, you compare each character with its counterpart from the opposite end of the string:
- The character at index
0is compared with the character at indexn - 1. - The character at index
1is compared with the character at indexn - 2. - And so on, moving inward from both ends.
The moment you find an index i where these two characters match, you return that index. Since you start checking from the beginning of the string, the first match you encounter is guaranteed to be the smallest such index.
If you go through all the positions and never find a matching pair, return -1.
How We Pick the Algorithm
Why Two pointers?
This problem maps to Two pointers through a short path in the full flowchart.
Scans from both ends of the string inward to find the first index where mirrored characters match, a classic two-pointer pattern.
Open in FlowchartIntuition
The problem asks for the smallest index i where s[i] matches its mirrored character s[n - i - 1]. The key observation is that since we want the smallest index, we should naturally start checking from the very beginning of the string and move forward. The first match we find will automatically be the smallest valid index.
Another important detail is that each index i is paired with its mirror n - i - 1. As i grows from 0 toward the middle, its mirror shrinks from n - 1 toward the middle. Once i passes the midpoint, the pairs simply repeat what we have already checked (just swapped). For example, the pair (i, n - i - 1) is the same pair as (n - i - 1, i). This means we only need to examine the first half of the string to cover every unique pair.
So the natural approach is to walk through indices from 0 up to the middle of the string, comparing each character with its mirror. The moment we find s[i] == s[n - i - 1], we return i immediately, knowing it is the smallest possible answer. If we finish scanning the first half without finding any match, then no such index exists, and we return -1.
Pattern Learn more about Two Pointers patterns.
Solution Approach
Solution 1: Simulation
We directly simulate the comparison process described in the problem.
The implementation steps are as follows:
-
Iterate over the first half of the string. We loop
ifrom0up tolen(s) // 2 + 1. We only need to scan the first half because each indexiand its mirrorn - i - 1form a unique pair, and pairs in the second half merely repeat those in the first half. -
Compare each character with its mirror. For each index
i, we check whethers[i] == s[-i - 1]. Here, the negative index-i - 1is a convenient way in Python to reference the mirrored positionn - i - 1, counting backward from the end of the string. -
Return the first match. Since we scan from the beginning, the first index
ithat satisfiess[i] == s[-i - 1]is guaranteed to be the smallest valid index. We return it immediately. -
Handle the no-match case. If the loop completes without finding any matching pair, we return
-1.
The reference code expresses this cleanly:
class Solution:
def firstMatchingIndex(self, s: str) -> int:
for i in range(len(s) // 2 + 1):
if s[i] == s[-i - 1]:
return i
return -1
Complexity Analysis:
- Time Complexity:
O(n), wherenis the length of the strings. In the worst case, we scan up to half of the string, which is still linear inn. - Space Complexity:
O(1), since we only use a constant amount of extra space for the loop variable, regardless of the input size.
Example Walkthrough
Let's trace through the solution using the string s = "abccba" (length n = 6).
We only need to scan the first half of the string, comparing each character at index i with its mirror at index n - i - 1 (or equivalently s[-i - 1] in Python). The loop runs i from 0 to len(s) // 2 = 3, inclusive of the boundary.
Setup:
Index: 0 1 2 3 4 5 Char: 'a' 'b' 'c' 'c' 'b' 'a' ↑mirror pairs↑
The mirror pairs are: (0, 5), (1, 4), (2, 3).
Step-by-step iteration:
i = 0: Compares[0]withs[-1]→s[0] = 'a',s[5] = 'a'.'a' == 'a'→ Match found! Return0immediately.
Since we start from the beginning and return on the first match, index 0 is guaranteed to be the smallest valid answer. The result is 0.
A second example where no early match occurs: s = "abcdef" (length n = 6).
Index: 0 1 2 3 4 5 Char: 'a' 'b' 'c' 'd' 'e' 'f'
i = 0:s[0] = 'a'vss[5] = 'f'→'a' != 'f', no match. Continue.i = 1:s[1] = 'b'vss[4] = 'e'→'b' != 'e', no match. Continue.i = 2:s[2] = 'c'vss[3] = 'd'→'c' != 'd', no match. Continue.i = 3:s[3] = 'd'vss[2] = 'c'→ this pair simply repeats(2, 3)swapped, still no match.
The loop ends without finding any matching pair, so the result is -1.
This illustrates both key behaviors: returning the smallest index on the first match, and returning -1 when no mirrored pair shares the same character.
Solution Implementation
1class Solution:
2 def firstMatchingIndex(self, s: str) -> int:
3 # Iterate from both ends toward the middle of the string.
4 # We only need to check up to the midpoint (inclusive for odd-length strings).
5 for i in range(len(s) // 2 + 1):
6 # Compare the character at the i-th position from the start
7 # with the character at the i-th position from the end.
8 if s[i] == s[-i - 1]:
9 # Return the first index where the two characters match.
10 return i
11 # If no matching pair is found, return -1.
12 return -1
13```
14
15A few notes on the changes:
16
17- **Python3 syntax**: The original code is already valid Python3, so no syntactic conversion was needed.
18- **Naming**: The method name `firstMatchingIndex` was preserved as instructed, and the parameter `s` and loop variable `i` follow standard, conventional naming for short-lived, single-purpose identifiers.
19- **Comments**: Added line-by-line explanations describing the two-pointer-style comparison and the return logic.
20
21If you'd like more descriptive variable names for readability (while keeping the method name unchanged), here's an alternative version:
22
23```python3
24class Solution:
25 def firstMatchingIndex(self, s: str) -> int:
26 string_length = len(s)
27 # Walk from both ends toward the center.
28 for index in range(string_length // 2 + 1):
29 # Character from the front vs. its mirror from the back.
30 if s[index] == s[string_length - index - 1]:
31 return index
32 return -1
331class Solution {
2 /**
3 * Finds the first index i (scanning from both ends toward the center)
4 * at which the character from the front matches the character from the back.
5 *
6 * @param s the input string
7 * @return the first matching index, or -1 if no such index exists
8 */
9 public int firstMatchingIndex(String s) {
10 // Total length of the string.
11 int length = s.length();
12
13 // Iterate from the start up to (and including) the middle of the string.
14 // The "+ 1" ensures the middle character is checked for odd-length strings.
15 for (int i = 0; i < length / 2 + 1; ++i) {
16 // Compare the character at position i (from the left)
17 // with its mirror character at position length - i - 1 (from the right).
18 if (s.charAt(i) == s.charAt(length - i - 1)) {
19 // Return the index as soon as a match is found.
20 return i;
21 }
22 }
23
24 // No matching index was found.
25 return -1;
26 }
27}
281class Solution {
2public:
3 int firstMatchingIndex(string s) {
4 int n = s.size();
5
6 // Compare characters from both ends moving toward the center.
7 // i goes from the start, while (n - i - 1) goes from the end.
8 // We iterate up to the middle (inclusive for odd-length strings).
9 for (int i = 0; i < n / 2 + 1; ++i) {
10 // If the character at the left index matches its mirrored
11 // character on the right, return the current index.
12 if (s[i] == s[n - i - 1]) {
13 return i;
14 }
15 }
16
17 // No matching mirrored pair was found.
18 return -1;
19 }
20};
211/**
2 * Finds the first index i (from the start) where the character at position i
3 * matches the character at the mirrored position from the end (n - i - 1).
4 *
5 * @param s - The input string to scan.
6 * @returns The first matching index, or -1 if no such match exists.
7 */
8function firstMatchingIndex(s: string): number {
9 // Cache the length of the string for repeated use.
10 const n: number = s.length;
11
12 // Iterate from the start up to (and including) the middle of the string.
13 // Adding 1 to the half-length ensures the middle index is checked for odd lengths.
14 for (let i = 0; i < Math.floor(n / 2) + 1; i++) {
15 // Compare the character at index i with its mirrored counterpart from the end.
16 if (s[i] === s[n - i - 1]) {
17 // Return the first index where the two characters are equal.
18 return i;
19 }
20 }
21
22 // No matching index was found.
23 return -1;
24}
25Time and Space Complexity
-
Time Complexity:
O(n), wherenis the length of the strings. Theforloop iterates from0tolen(s) // 2, performing at mostn / 2 + 1iterations. Each iteration does a constant-time character comparisons[i] == s[-i - 1], so the total time is proportional ton, givingO(n). -
Space Complexity:
O(1). Only a constant amount of extra space is used for the loop variablei, with no additional data structures that scale with the input size.
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-one error in the loop range causing a redundant or out-of-range check
A frequent mistake is misjudging how far the loop should run. Developers often write one of these incorrect bounds:
range(len(s) // 2)— stops too early, missing the middle character of an odd-length string.range(len(s))— runs too far, re-checking every pair a second time from the opposite side (wasteful, and conceptually confusing).
Why the middle matters: For an odd-length string, the exact center index n // 2 is its own mirror (s[i] == s[n - i - 1] becomes s[i] == s[i]), which is always a match. If the smallest matching index happens to be this center position, a loop bounded by range(len(s) // 2) will skip it and incorrectly return -1.
Example: For s = "abcxy" (length 5):
- Index 0:
'a'vs'y'→ no match - Index 1:
'b'vs'x'→ no match - Index 2:
'c'vs'c'(the center) → match! Should return2.
With range(5 // 2) = range(2), the loop only checks indices 0 and 1, then wrongly returns -1.
Solution: Use range(len(s) // 2 + 1) to include the center index for odd lengths. The + 1 guarantees the midpoint is always examined:
class Solution:
def firstMatchingIndex(self, s: str) -> int:
n = len(s)
for i in range(n // 2 + 1): # +1 ensures the center is checked
if s[i] == s[n - i - 1]:
return i
return -1
For even-length strings, the extra iteration at i = n // 2 simply re-checks a pair already covered, so it is harmless to correctness (and can be tightened if desired).
Pitfall 2: Confusing the negative-index trick s[-i - 1]
The concise version uses s[-i - 1] to reference the mirror position. A common slip is writing s[-i] instead, which is off by one:
- When
i = 0,s[-i]becomess[0](the first character compared with itself), but the intended mirror iss[-1](the last character).
Solution: Always use s[-i - 1], or avoid the trick entirely with the explicit form s[n - i - 1], which is easier to verify:
if s[i] == s[n - i - 1]: # explicit, unambiguous return i
Pitfall 3: Misreading the problem as a palindrome check
The task only asks for the first matching pair, not whether the entire string is a palindrome. A common error is returning a boolean or requiring all pairs to match before returning a result. Here, the moment a single pair matches, you return that index immediately — partial matches are exactly what we want.
Solution: Return as soon as the first match is found inside the loop; do not accumulate or require full-string symmetry.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhat is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
Recommended Readings
Two Pointers Technique Explained If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe width 560 height 315 src https www youtube nocookie com embed rQhNcycbf8w si lE7qtd1h_JSQwGpW title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!