Facebook Pixel

3884. First Matching Character From Both Ends

EasyTwo PointersString
LeetCode ↗

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 0 is compared with the character at index n - 1.
  • The character at index 1 is compared with the character at index n - 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Two pointers?

This problem maps to Two pointers through a short path in the full flowchart.

Findspecificindices?yesMonotonicproperty?yesTwo pointers

Scans from both ends of the string inward to find the first index where mirrored characters match, a classic two-pointer pattern.

Open in Flowchart

Intuition

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:

  1. Iterate over the first half of the string. We loop i from 0 up to len(s) // 2 + 1. We only need to scan the first half because each index i and its mirror n - i - 1 form a unique pair, and pairs in the second half merely repeat those in the first half.

  2. Compare each character with its mirror. For each index i, we check whether s[i] == s[-i - 1]. Here, the negative index -i - 1 is a convenient way in Python to reference the mirrored position n - i - 1, counting backward from the end of the string.

  3. Return the first match. Since we scan from the beginning, the first index i that satisfies s[i] == s[-i - 1] is guaranteed to be the smallest valid index. We return it immediately.

  4. 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), where n is the length of the string s. In the worst case, we scan up to half of the string, which is still linear in n.
  • 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: Compare s[0] with s[-1]s[0] = 'a', s[5] = 'a'.
    • 'a' == 'a'Match found! Return 0 immediately.

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' vs s[5] = 'f''a' != 'f', no match. Continue.
  • i = 1: s[1] = 'b' vs s[4] = 'e''b' != 'e', no match. Continue.
  • i = 2: s[2] = 'c' vs s[3] = 'd''c' != 'd', no match. Continue.
  • i = 3: s[3] = 'd' vs s[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
33
1class 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}
28
1class 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};
21
1/**
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}
25

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the string s. The for loop iterates from 0 to len(s) // 2, performing at most n / 2 + 1 iterations. Each iteration does a constant-time character comparison s[i] == s[-i - 1], so the total time is proportional to n, giving O(n).

  • Space Complexity: O(1). Only a constant amount of extra space is used for the loop variable i, 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 return 2.

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] becomes s[0] (the first character compared with itself), but the intended mirror is s[-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 Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More