3696. Maximum Distance Between Unequal Words in Array I 🔒
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 condition:
words[i] != words[j]— the words at these two indices must be different.
The distance between indices i and j is defined as j - i + 1, which is essentially the length of the subarray spanning from index i to index j (inclusive).
Return the maximum distance among all such valid pairs. If no valid pair exists (meaning all words in the array are identical), return 0.
Example:
Suppose words = ["a", "b", "a", "a"]:
- The pair
(i = 0, j = 1)is valid since"a" != "b", giving a distance of1 - 0 + 1 = 2. - The pair
(i = 1, j = 3)is valid since"b" != "a", giving a distance of3 - 1 + 1 = 3.
The maximum distance is 3.
If words = ["a", "a", "a"], every pair of words is identical, so no valid pair exists and the answer is 0.
Key points to keep in mind:
- The two indices must point to different words — comparing a word with an identical copy of itself does not count.
- The distance formula
j - i + 1means a pair of adjacent differing words already yields a distance of2. - To maximize the distance, you want the two differing words to be as far apart as possible, ideally near the two ends of the array.
How We Pick the Algorithm
Why Two pointers?
This problem maps to Two pointers through a short path in the full flowchart.
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 FlowchartIntuition
A brute-force approach would check every pair (i, j) and track the maximum j - i + 1, but that takes O(n^2) time. To do better, we need a key observation about where the optimal pair must be located.
Since we want to maximize j - i + 1, we naturally want i to be as small as possible and j to be as large as possible. The extreme case is i = 0 and j = n - 1, giving a distance of n. This leads us to ask: does the optimal pair always involve one of the two endpoints of the array?
The answer is yes, and we can see why through a proof by contradiction:
- Suppose the optimal pair is
(i, j)where neither index is an endpoint, i.e.,0 < iandj < n - 1. - If
words[0] != words[j], then the pair(0, j)would be valid and have a larger distance than(i, j)— contradicting that(i, j)is optimal. So it must be thatwords[0] == words[j]. - Similarly, if
words[i] != words[n - 1], the pair(i, n - 1)would be larger. So it must be thatwords[i] == words[n - 1]. - But now look at the endpoints themselves:
words[0] == words[j]andwords[n - 1] == words[i], and we knowwords[i] != words[j]. This forceswords[0] != words[n - 1], making(0, n - 1)a valid pair with distancen— strictly larger thanj - i + 1. Contradiction again.
Therefore, at least one element of the optimal pair must sit at index 0 or index n - 1.
This insight collapses the problem dramatically: instead of checking all pairs, we only need to compare every word against the first word and the last word:
- If
words[i] != words[0], the pair(0, i)gives distancei + 1. - If
words[i] != words[n - 1], the pair(i, n - 1)gives distancen - i.
Taking the maximum over all such candidates yields the answer in a single O(n) pass with O(1) extra space. If no word differs from either endpoint, all words are identical, and the answer naturally stays at 0.
Solution Approach
Based on the insight that the optimal pair must include either the first or the last element, we use a single-pass scan that compares every word against the two endpoints of the array.
Step-by-Step Walkthrough
Step 1 — Setup
Record the array length n and initialize the answer:
n = len(words)
ans = 0
Starting ans at 0 handles the edge case where all words are identical — no comparison will ever update it, so 0 is returned as required.
Step 2 — Scan and compare against both endpoints
For each index i from 0 to n - 1, perform two checks:
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)
- Check against the first word: If
words[i] != words[0], then(0, i)is a valid pair. Its distance isi - 0 + 1 = i + 1. - Check against the last word: If
words[i] != words[-1], then(i, n - 1)is a valid pair. Its distance is(n - 1) - i + 1 = n - i.
Each valid candidate updates ans via max.
Step 3 — Return the result
return ans
Why This Covers All Cases
From the proof in the intuition, every optimal pair must anchor at index 0 or index n - 1. The loop exhaustively enumerates:
- All pairs of the form
(0, i)— covered by the first check. - All pairs of the form
(i, n - 1)— covered by the second check.
The special pair (0, n - 1) is covered by both checks (when i = n - 1 in the first check, or i = 0 in the second), so no candidate is missed.
Trace Example
Take words = ["a", "b", "a", "a"], so n = 4:
i | words[i] | vs words[0] = "a" | vs words[-1] = "a" | ans |
|---|---|---|---|---|
| 0 | "a" | equal — skip | equal — skip | 0 |
| 1 | "b" | differ → i + 1 = 2 | differ → n - i = 3 | 3 |
| 2 | "a" | equal — skip | equal — skip | 3 |
| 3 | "a" | equal — skip | equal — skip | 3 |
Final answer: 3, matching the pair (1, 3).
Complete Code
class Solution:
def maxDistance(self, words: List[str]) -> int:
n = len(words)
ans = 0
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)
return ans
Complexity Analysis
- Time Complexity:
O(n × m), wherenis the number of words andmis the average word length — each of theniterations performs two string comparisons costing up toO(m). If string comparison is treated asO(1), this is simplyO(n). - Space Complexity:
O(1)— only the variablesnandansare used; no auxiliary data structures are needed.
Example Walkthrough
Let's trace the algorithm with words = ["a", "b", "a", "b", "a"], where n = 5.
Here the first word is words[0] = "a" and the last word is words[-1] = "a". Notice the two endpoints are identical, so the trivial pair (0, 4) with distance 5 is not valid — the answer must come from an inner element paired with one of the endpoints.
We initialize ans = 0 and scan each index, comparing against both endpoints:
i | words[i] | vs words[0] = "a" | vs words[-1] = "a" | ans |
|---|---|---|---|---|
| 0 | "a" | equal — skip | equal — skip | 0 |
| 1 | "b" | differ → pair (0, 1), distance i + 1 = 2 | differ → pair (1, 4), distance n - i = 4 | 4 |
| 2 | "a" | equal — skip | equal — skip | 4 |
| 3 | "b" | differ → pair (0, 3), distance i + 1 = 4 | differ → pair (3, 4), distance n - i = 2 | 4 |
| 4 | "a" | equal — skip | equal — skip | 4 |
Step-by-step highlights:
- At
i = 1, the word"b"differs from both endpoints. Anchoring at the first word gives the pair(0, 1)with distance2, but anchoring at the last word gives the much better pair(1, 4)with distance5 - 1 = 4. The answer jumps to4. - At
i = 3,"b"again differs from both endpoints. This time the first-word anchor is the stronger candidate: pair(0, 3)yields distance3 + 1 = 4. It ties the current best, soansremains4. - Indices
0,2, and4hold"a", matching both endpoints, so they contribute nothing.
Final answer: 4, achieved by either (1, 4) — "b" vs "a" — or (0, 3) — "a" vs "b".
This example illustrates the core insight nicely: even though the global endpoints (0, 4) form an invalid pair (both are "a"), the optimal valid pair still anchors at one of the two endpoints, which is exactly why a single pass comparing each word against words[0] and words[-1] suffices.
Solution Implementation
1class Solution:
2 def maxDistance(self, words: List[str]) -> int:
3 # Total number of words in the list
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)
11 if words[i] != words[0]:
12 max_dist = max(max_dist, i + 1)
13
14 # If the current word differs from the last word,
15 # the distance between index i and the last index is (n - 1 - i + 1)
16 if words[i] != words[-1]:
17 max_dist = max(max_dist, num_words - i)
18
19 return max_dist
201class Solution {
2 public int maxDistance(String[] words) {
3 // Total number of words in the array
4 int wordCount = words.length;
5 // Stores the maximum distance found so far
6 int maxDist = 0;
7
8 // Iterate through every word in the array
9 for (int index = 0; index < wordCount; ++index) {
10 // If the current word differs from the first word,
11 // the distance between them is (index - 0 + 1) = index + 1
12 if (!words[index].equals(words[0])) {
13 maxDist = Math.max(maxDist, index + 1);
14 }
15 // If the current word differs from the last word,
16 // the distance between them is (wordCount - 1 - index + 1) = wordCount - index
17 if (!words[index].equals(words[wordCount - 1])) {
18 maxDist = Math.max(maxDist, wordCount - index);
19 }
20 }
21
22 // Return the maximum distance between two distinct words
23 return maxDist;
24 }
25}
261class Solution {
2public:
3 int maxDistance(vector<string>& words) {
4 int size = words.size();
5 int maxDist = 0;
6
7 for (int i = 0; i < size; ++i) {
8 // If the current word differs from the first word,
9 // the distance between index 0 and index i is (i + 1).
10 if (words[i] != words[0]) {
11 maxDist = max(maxDist, i + 1);
12 }
13
14 // If the current word differs from the last word,
15 // the distance between index i and index (size - 1) is (size - i).
16 if (words[i] != words[size - 1]) {
17 maxDist = max(maxDist, size - i);
18 }
19 }
20
21 return maxDist;
22 }
23};
241/**
2 * Finds the maximum distance between two indices i and j
3 * such that words[i] !== words[j].
4 *
5 * Key insight: one of the optimal endpoints must be either
6 * the first word or the last word of the array. So we only
7 * need to compare each word against words[0] and words[n - 1].
8 *
9 * Time complexity: O(n) — single pass through the array
10 * Space complexity: O(1) — only constant extra variables
11 *
12 * @param words - the input array of strings
13 * @returns the maximum distance (j - i + 1) between two different words,
14 * or 0 if all words are identical
15 */
16function maxDistance(words: string[]): number {
17 const n: number = words.length;
18 let ans: number = 0;
19
20 for (let i = 0; i < n; i++) {
21 // If the current word differs from the first word,
22 // the distance from index 0 to index i is (i + 1).
23 if (words[i] !== words[0]) {
24 ans = Math.max(ans, i + 1);
25 }
26
27 // If the current word differs from the last word,
28 // the distance from index i to index (n - 1) is (n - i).
29 if (words[i] !== words[n - 1]) {
30 ans = Math.max(ans, n - i);
31 }
32 }
33
34 return ans;
35}
36Time and Space Complexity
-
Time Complexity:
O(n), wherenis the length of the arraywords. The code iterates through the array exactly once with a singleforloop. In each iteration, it performs a constant number of operations: two string comparisons (words[i] != words[0]andwords[i] != words[-1]) and at most twomaxupdates onans. Since each iteration costsO(1)and there areniterations, the overall time complexity isO(n). -
Space Complexity:
O(1). The algorithm only uses a fixed number of extra variables (n,ans, and the loop indexi), regardless of the input size. No additional data structures are allocated, so the extra space used is constant.
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Using j - i Instead of j - i + 1 as the Distance
A frequent off-by-one mistake is treating the distance as the gap between indices rather than the length of the subarray:
# WRONG: computes j - i instead of j - i + 1
for i in range(n):
if words[i] != words[0]:
max_dist = max(max_dist, i) # should be i + 1
if words[i] != words[-1]:
max_dist = max(max_dist, n - i - 1) # should be n - i
For words = ["a", "b"], this returns 1 instead of the correct 2. The problem defines distance as j - i + 1 (inclusive length), so two adjacent differing words already produce a distance of 2, not 1.
Solution: Carefully translate the formula. The pair (0, i) has distance i - 0 + 1 = i + 1, and the pair (i, n - 1) has distance (n - 1) - i + 1 = n - i. A quick sanity check with a two-element array like ["a", "b"] (expected output 2) immediately exposes this bug.
Pitfall 2: Breaking Out of the Loop Too Early
It is tempting to "optimize" by stopping at the first mismatch:
# WRONG: early exit misses the better anchor
for i in range(n):
if words[i] != words[0]:
return i + 1 # exits before checking the last-word anchor
For words = ["a", "b", "a", "a"], this returns 2 (pair (0, 1)), missing the optimal answer 3 from pair (1, 3). The first mismatch against words[0] is not necessarily part of the maximum-distance pair — the optimal pair may anchor at the last element instead.
Solution: Either scan the full array updating max(ans, ...) for both anchors (as the provided code does), or use the two-pointer variant correctly: scan forward to find the first index differing from words[-1] (candidate n - i), and scan backward to find the last index differing from words[0] (candidate j + 1), then take the maximum of the two. Both anchors must always be considered before returning.
Pitfall 3: Comparing Only words[0] and words[-1] and Returning Early
Another shortcut that fails is assuming that if the two endpoints differ you are done, and if they are equal the answer is 0:
# WRONG: only handles one special case if words[0] != words[-1]: return n return 0 # incorrect — interior elements may still differ from the endpoints
For words = ["a", "b", "a"], the endpoints are equal, but the pair (0, 1) or (1, 2) gives a valid distance of 2. The correct insight is that the optimal pair must include one endpoint — not that it must consist of both endpoints.
Solution: Keep the early return for words[0] != words[-1] (returning n is valid there as it is the maximum possible distance), but when the endpoints are equal, you must still scan the interior elements against the endpoints, exactly as the full loop does.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhich data structure is used in a depth first search?
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 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
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 describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!