Facebook Pixel

3706. Maximum Distance Between Unequal Words in Array II šŸ”’

MediumArrayString
LeetCode ↗

Problem Description

You are given a string array words.

Your task is to find the maximum distance between two distinct indices i and j that satisfy the following conditions:

  • words[i] != words[j] — the words at the two indices must be different, and
  • the distance between them is defined as j - i + 1.

Return the maximum distance among all such valid pairs. If no valid pair exists (for example, when all words in the array are identical), return 0.

Key points to understand:

  1. The distance is not simply j - i; it is j - i + 1, which counts the number of elements in the range from index i to index j (inclusive).
  2. The two indices must hold different words. Pairs where words[i] == words[j] do not count.
  3. To maximize j - i + 1, we want i to be as small as possible and j to be as large as possible, while still keeping words[i] and words[j] different.

Example:

  • If words = ["a", "b", "c", "a"], the pair (i = 0, j = 2) gives words[0] = "a" and words[2] = "c", which are different, so the distance is 2 - 0 + 1 = 3. The pair (i = 1, j = 3) also gives a distance of 3. The pair (i = 0, j = 3) is invalid because both words are "a". Hence the answer is 3.
  • If words = ["x", "x", "x"], every pair has equal words, so no valid pair exists, and the answer is 0.
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.

Tree orgraph?noTwopointers /findyesTwo pointers

The optimal pair must anchor at either index 0 or n-1, reducing the search to comparing each word against both endpoints in one pass.

Open in Flowchart

Intuition

A brute-force approach would check every pair (i, j) and take the maximum distance among pairs with different words, costing O(n^2) time. But a key observation lets us do much better.

To maximize j - i + 1, we want i to be small and j to be large. The most extreme candidates are the two ends of the array: index 0 and index n - 1.

Claim: in any optimal pair, at least one of the two indices must be an endpoint of the array (0 or n - 1).

Why? Suppose the best pair (i, j) has neither index at an endpoint, i.e., 0 < i and j < n - 1. Then:

  • words[0] must equal words[j] — otherwise the pair (0, j) would be valid and longer, contradicting optimality.
  • words[n - 1] must equal words[i] — otherwise the pair (i, n - 1) would be valid and longer.

But since words[i] != words[j], combining the two equalities gives words[n - 1] != words[0]. That makes (0, n - 1) itself a valid pair with distance n, the largest possible — again contradicting the assumption that (i, j) was optimal.

So the assumption fails, and every optimal answer involves words[0] or words[n - 1].

This immediately suggests a single-pass strategy: for each index i, only two pairings matter —

  • pair i with the first word: if words[i] != words[0], the distance is i - 0 + 1 = i + 1;
  • pair i with the last word: if words[i] != words[n - 1], the distance is (n - 1) - i + 1 = n - i.

Taking the maximum over all such candidates covers every possible optimal pair, reducing the problem to a simple O(n) scan with O(1) extra space.

Solution Approach

Based on the observation that at least one index of the optimal pair must be an endpoint, the implementation becomes a single pass over the array, comparing every word against the first word and the last word.

Step-by-Step Walkthrough

Step 1: Initialization

n = len(words)
ans = 0
  • n is the length of the array.
  • ans holds the best distance found so far. Starting it at 0 automatically handles the "no valid pair exists" case — if every comparison fails, ans is never updated and 0 is returned.

Step 2: Single pass — compare each word with both endpoints

for i in range(n):
    if words[i] != words[0]:
        ans = max(ans, i + 1)
    if words[i] != words[-1]:
        ans = max(ans, n - i)

For each index i, two candidate pairs are evaluated:

  1. Pair (0, i) — if words[i] != words[0], this is a valid pair, and its distance is i - 0 + 1 = i + 1.
  2. Pair (i, n - 1) — if words[i] != words[-1] (i.e., words[n - 1]), this is a valid pair, and its distance is (n - 1) - i + 1 = n - i.

Each valid candidate is folded into ans via max.

Two boundary details worth noting:

  • When i = 0, the first condition words[0] != words[0] is always false, so the invalid "pair" of an index with itself is never counted. The same holds for i = n - 1 in the second condition.
  • If words[0] != words[n - 1], the loop naturally captures the maximum possible answer n — for example, at i = n - 1 the first check yields (n - 1) + 1 = n.

Step 3: Return the result

return ans

Why This Covers All Cases

The proof from the intuition guarantees that any optimal pair (i, j) has i = 0 or j = n - 1:

  • If i = 0, the pair is found by the first if when the loop reaches index j.
  • If j = n - 1, the pair is found by the second if when the loop reaches index i.

So no optimal answer can be missed by this scan.

Data Structures and Patterns

  • No auxiliary data structures are needed — just two scalar variables.
  • The pattern is a greedy / endpoint-anchoring technique: instead of enumerating all O(n^2) pairs, a structural property of the optimum shrinks the candidate set to O(n) pairs anchored at the array's two ends.

Complexity Analysis

  • Time complexity: O(n) — a single traversal of the array, with each iteration doing constant work (two string comparisons; if string lengths are bounded by a constant, each comparison is O(1)).
  • Space complexity: O(1) — only n and ans are stored, independent of input size.

Alternative Perspective: Two-Pointer Variant

The same endpoint insight admits another O(n) formulation. Find the largest j with words[j] != words[0] and the smallest i with words[i] != words[n - 1]; the answer is max(j + 1, n - i) (or 0 if neither exists). This computes identical results, since both methods exhaust exactly the pairs anchored at an endpoint — the single-pass version above simply merges both searches into one loop.

Example Walkthrough

Let's trace the single-pass solution on words = ["a", "b", "c", "a"].

Setup:

  • n = 4
  • First word: words[0] = "a"
  • Last word: words[-1] = "a" (i.e., words[3])
  • ans = 0

Loop execution:

iwords[i]Check 1: words[i] != words[0]?Candidate i + 1Check 2: words[i] != words[-1]?Candidate n - ians after
0"a""a" != "a" → No—"a" != "a" → No—0
1"b""b" != "a" → Yes1 + 1 = 2"b" != "a" → Yes4 - 1 = 33
2"c""c" != "a" → Yes2 + 1 = 3"c" != "a" → Yes4 - 2 = 23
3"a""a" != "a" → No—"a" != "a" → No—3

Detailed reasoning per step:

  • i = 0: Both checks compare "a" against "a", so neither passes. This is exactly how the algorithm avoids pairing an index with itself — no update occurs.
  • i = 1: words[1] = "b" differs from the first word, giving valid pair (0, 1) with distance 2. It also differs from the last word, giving valid pair (1, 3) with distance 4 - 1 = 3. Now ans = 3.
  • i = 2: words[2] = "c" differs from the first word, giving pair (0, 2) with distance 3 — ties the current best. It also differs from the last word, giving pair (2, 3) with distance 2 — no improvement. ans stays 3.
  • i = 3: words[3] = "a" matches both endpoints, so the invalid pair (0, 3) (both "a") is correctly skipped.

Result: ans = 3 āœ“

This matches the expected answer: the optimal pairs are (0, 2) → ["a", ..., "c"] and (1, 3) → ["b", ..., "a"], both with distance 3. Notice how every candidate the loop evaluated was anchored at index 0 or index n - 1, confirming the endpoint observation — the invalid full-range pair (0, 3) was the only endpoint pair rejected.

Edge case check — words = ["x", "x", "x"]:

Every comparison words[i] != "x" fails at all three indices, so ans is never updated and the function returns 0, correctly reporting that no valid pair exists.

Solution Implementation

1class Solution:
2    def maxDistance(self, words: List[str]) -> int:
3        # Total number of words in the array
4        num_words = len(words)
5        # Maximum distance found so far
6        max_dist = 0
7
8        for i in range(num_words):
9            # If the current word differs from the first word,
10            # the distance between index 0 and index i is (i - 0 + 1) = i + 1
11            if words[i] != words[0]:
12                max_dist = max(max_dist, i + 1)
13            # If the current word differs from the last word,
14            # the distance between index i and the last index is (num_words - 1 - i + 1) = num_words - i
15            if words[i] != words[-1]:
16                max_dist = max(max_dist, num_words - i)
17
18        return max_dist
19
1class Solution {
2    public int maxDistance(String[] words) {
3        // Total number of words in the array
4        int wordCount = words.length;
5
6        // Tracks the maximum distance found so far
7        int maxDistance = 0;
8
9        // Iterate through every word once
10        for (int index = 0; index < wordCount; ++index) {
11            // If the current word differs from the FIRST word,
12            // the span from index 0 to the current index is a valid candidate.
13            // Its length is (index - 0 + 1) = index + 1.
14            if (!words[index].equals(words[0])) {
15                maxDistance = Math.max(maxDistance, index + 1);
16            }
17
18            // If the current word differs from the LAST word,
19            // the span from the current index to index (wordCount - 1) is a valid candidate.
20            // Its length is (wordCount - 1 - index + 1) = wordCount - index.
21            if (!words[index].equals(words[wordCount - 1])) {
22                maxDistance = Math.max(maxDistance, wordCount - index);
23            }
24        }
25
26        // Returns 0 if all words are identical (no valid pair exists)
27        return maxDistance;
28    }
29}
30
1class Solution {
2public:
3    int maxDistance(vector<string>& words) {
4        int wordCount = words.size();
5        int maxDist = 0;
6
7        // Iterate over every word, treating it as a potential endpoint
8        // of a pair of unequal words.
9        for (int i = 0; i < wordCount; ++i) {
10            // If the current word differs from the first word,
11            // the pair (0, i) is valid; its distance is i - 0 + 1.
12            if (words[i] != words[0]) {
13                maxDist = max(maxDist, i + 1);
14            }
15
16            // If the current word differs from the last word,
17            // the pair (i, wordCount - 1) is valid; its distance is
18            // (wordCount - 1) - i + 1 = wordCount - i.
19            if (words[i] != words[wordCount - 1]) {
20                maxDist = max(maxDist, wordCount - i);
21            }
22        }
23
24        // If all words are identical, maxDist remains 0.
25        return maxDist;
26    }
27};
28
1/**
2 * Finds the maximum distance between two indices i and j
3 * such that words[i] !== words[j].
4 *
5 * Key insight: the optimal pair must involve either the first
6 * or the last element of the array. If words[i] differs from
7 * words[0], the distance is i + 1 (1-based span); if words[i]
8 * differs from words[n - 1], the distance is n - i.
9 *
10 * @param words - the input array of strings
11 * @returns the maximum distance between two unequal words,
12 *          or 0 if all words are identical
13 */
14function maxDistance(words: string[]): number {
15    // Total number of words in the array
16    const n: number = words.length;
17    // Holds the maximum distance found so far
18    let ans: number = 0;
19
20    // Iterate over every word and compare it against
21    // the first and the last word of the array
22    for (let i = 0; i < n; i++) {
23        // If the current word differs from the first word,
24        // the candidate distance is i + 1
25        if (words[i] !== words[0]) {
26            ans = Math.max(ans, i + 1);
27        }
28        // If the current word differs from the last word,
29        // the candidate distance is n - i
30        if (words[i] !== words[n - 1]) {
31            ans = Math.max(ans, n - i);
32        }
33    }
34
35    // Return the maximum distance (0 if no unequal pair exists)
36    return ans;
37}
38

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the array words. The code iterates through the array exactly once with a single loop. In each iteration, it performs a constant number of operations: two string comparisons (words[i] != words[0] and words[i] != words[-1]) and two max updates. Therefore, the total work scales linearly with the size of the input.

  • Space Complexity: O(1). The algorithm only uses a fixed number of extra variables (n, ans, and the loop index i), regardless of the input size. No additional data structures are allocated, so the auxiliary space remains constant.

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Computing the distance as j - i instead of j - i + 1

This is the single most frequent mistake. The problem defines distance as the count of elements in the inclusive range [i, j], not the index difference.

Incorrect:

for i in range(num_words):
    if words[i] != words[0]:
        max_dist = max(max_dist, i)            # off by one
    if words[i] != words[-1]:
        max_dist = max(max_dist, num_words - 1 - i)  # off by one

For words = ["a", "b", "c", "a"], this returns 2 instead of the correct 3. The bug is easy to miss because the code still "looks symmetric" and passes trivial cases like all-identical arrays (which return 0 either way).

Fix: Always translate the pair (i, j) into j - i + 1:

  • Pair (0, i) → distance i + 1
  • Pair (i, n - 1) → distance n - i

A quick sanity check: when words[0] != words[n - 1], the answer must be exactly n (the whole array). If your formula yields n - 1 for that case, you have an off-by-one error.


Pitfall 2: Assuming the optimal pair must be (0, n - 1) and only checking the two endpoints against each other

A tempting "shortcut" is:

return num_words if words[0] != words[-1] else 0   # WRONG

This fails whenever the first and last words are equal but a valid pair still exists, e.g. words = ["a", "b", "a"]. Here words[0] == words[2], yet (0, 1) and (1, 2) are both valid with distance 2. The correct structural claim is weaker: at least one index of an optimal pair is an endpoint — not both. You still must scan all indices and anchor each against the first word and the last word separately.


Pitfall 3: Comparing only against words[0] (or only against words[-1])

Checking just one anchor breaks symmetry:

for i in range(num_words):
    if words[i] != words[0]:
        max_dist = max(max_dist, i + 1)   # missing the second check

For words = ["b", "a", "a", "a"], the largest index differing from words[0] is i = 3, giving the correct 4. But for words = ["a", "a", "a", "b"] mirrored cases such as ["a", "b", "b", "b"] still work — the real failure shows up in inputs like ["a", "b", "a", "a"]: the only index differing from words[0] is i = 1, yielding 2, while the true answer is 3 from pair (1, 3) anchored at the last element. Both endpoint checks are required to cover both halves of the proof.


Pitfall 4: Brute-forcing all pairs and hitting O(n²) time limits

for i in range(num_words):
    for j in range(i + 1, num_words):
        if words[i] != words[j]:
            max_dist = max(max_dist, j - i + 1)

This is correct but quadratic, and on large inputs (e.g., n up to 10^5) it times out. The endpoint-anchoring insight reduces the candidate set to O(n) pairs. If you do start with brute force, an easy partial optimization — breaking the inner loop from the right side at the first mismatch — already hints at the linear solution.


Pitfall 5: Forgetting the "all identical" / single-element edge cases

If every word is the same (including n = 1), no valid pair exists and the answer must be 0. Solutions that initialize the answer to 1, or that unconditionally return num_words after some check, fail here. Initializing max_dist = 0 and only updating it on a confirmed mismatch handles this for free — verify with words = ["x", "x", "x"] (expect 0) and words = ["x"] (expect 0).

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:

A heap is a ...?


Recommended Readings

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

Load More