Facebook Pixel

2399. Check Distances Between Same Letters

EasyArrayHash TableString
Leetcode Link

Problem Description

You are given a string s where every lowercase letter that appears in the string appears exactly twice. You are also given an array distance of length 26, where each position corresponds to a letter of the alphabet ('a' at index 0, 'b' at index 1, and so on).

A string is considered "well-spaced" if, for each letter that appears in the string, the number of letters between its two occurrences equals the value specified in the distance array for that letter.

For example, if the letter 'a' appears at positions 1 and 4 in the string, there are 2 letters between them (positions 2 and 3). This would be valid only if distance[0] = 2.

Your task is to determine if the given string s is well-spaced according to the distance array. Return true if it is well-spaced, otherwise return false.

Key points to understand:

  • Each letter in s appears exactly twice
  • The distance between two occurrences of a letter is the count of letters between them (not including the letter itself)
  • If a letter doesn't appear in s, its corresponding value in distance can be ignored
  • Letters are mapped to indices: 'a' → 0, 'b' → 1, ..., 'z' → 25
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since each letter appears exactly twice in the string, we need to track where we first see each letter and then verify the distance when we encounter it the second time.

The key insight is that we don't need to store both positions for each letter. When we see a letter for the first time, we just record its position. When we see it again, we can immediately calculate the distance between the two occurrences and check if it matches the required distance.

Think of it like marking checkpoints: as we traverse the string from left to right, we place a marker when we first see a letter. When we see that same letter again, we measure how far we've traveled since the marker and verify if it matches our expected distance.

The distance between two occurrences at positions i and j (where j > i) is j - i - 1. For example, if a letter appears at position 2 and position 5, the distance is 5 - 2 - 1 = 2 (there are 2 letters between them).

This leads to a single-pass solution: iterate through the string once, and for each character:

  • If we haven't seen it before, record its position
  • If we have seen it before, calculate the distance and verify it matches the expected value
  • If any distance doesn't match, we can immediately return false
  • If we complete the traversal without issues, the string is well-spaced

Using a hash table or array to store the first occurrence positions makes this checking process efficient with O(n) time complexity where n is the length of the string.

Solution Approach

The solution uses a hash table (or dictionary) to track the first occurrence of each letter and validates the distance when encountering the second occurrence.

Implementation walkthrough:

  1. Initialize a hash table d using defaultdict(int) to store the position of the first occurrence of each letter. The default value of 0 helps us identify if we've seen a letter before.

  2. Iterate through the string using enumerate(map(ord, s), 1):

    • We use enumerate starting from 1 (not 0) to make position tracking easier
    • map(ord, s) converts each character to its ASCII value for easier calculation
    • For each character at position i, we get its ASCII value c
  3. Convert character to alphabet index: Calculate j = c - ord("a") to get the 0-based index for the letter (0 for 'a', 1 for 'b', etc.)

  4. Check if this is the second occurrence:

    • If d[j] is non-zero, we've seen this letter before
    • Calculate the distance: i - d[j] - 1
      • i is the current position (1-indexed)
      • d[j] is the first occurrence position (1-indexed)
      • Subtract 1 to get the number of letters between them
    • Compare with distance[j] - if they don't match, return False
  5. Record the position: If this is the first occurrence (or after checking the second), set d[j] = i

  6. Return the result: If we complete the iteration without finding any mismatches, return True

Example walkthrough: For string "abaccb" with distance = [2, 3, 0, ...]:

  • Position 1: 'a' → Store d[0] = 1
  • Position 2: 'b' → Store d[1] = 2
  • Position 3: 'a' → Check: 3 - 1 - 1 = 1 ≠ 2, return False

The algorithm runs in O(n) time where n is the length of the string, with O(1) space since we store at most 26 letters.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a concrete example to understand how it works.

Example Input:

  • String s = "abccba"
  • Distance array distance = [4, 2, 1, ...] (showing first 3 values for letters a, b, c)

Step-by-step execution:

Starting with an empty hash table d = {} to track first occurrences.

Position 1 (index 0): Character 'a'

  • Convert 'a' to index: j = ord('a') - ord('a') = 0
  • Check d[0]: It's 0 (not seen before)
  • Store first occurrence: d[0] = 1
  • Hash table: d = {0: 1}

Position 2 (index 1): Character 'b'

  • Convert 'b' to index: j = ord('b') - ord('a') = 1
  • Check d[1]: It's 0 (not seen before)
  • Store first occurrence: d[1] = 2
  • Hash table: d = {0: 1, 1: 2}

Position 3 (index 2): Character 'c'

  • Convert 'c' to index: j = ord('c') - ord('a') = 2
  • Check d[2]: It's 0 (not seen before)
  • Store first occurrence: d[2] = 3
  • Hash table: d = {0: 1, 1: 2, 2: 3}

Position 4 (index 3): Character 'c' (second occurrence)

  • Convert 'c' to index: j = ord('c') - ord('a') = 2
  • Check d[2]: It's 3 (seen before at position 3)
  • Calculate distance: 4 - 3 - 1 = 0
  • Compare with distance[2] = 1: 0 ≠ 1
  • Return False - The string is not well-spaced!

The algorithm detected that the two 'c' characters are adjacent (0 letters between them), but the distance array requires 1 letter between them. Therefore, the string "abccba" is not well-spaced according to the given distance requirements.

Alternative scenario: If the string were "abcbca" with the same distance array:

  • 'a' appears at positions 1 and 6: distance = 6 - 1 - 1 = 4 ✓ (matches distance[0])
  • 'b' appears at positions 2 and 4: distance = 4 - 2 - 1 = 1 ✗ (doesn't match distance[1] = 2)
  • Result: False

This demonstrates how the algorithm efficiently checks distances in a single pass through the string.

Solution Implementation

1from collections import defaultdict
2from typing import List
3
4class Solution:
5    def checkDistances(self, s: str, distance: List[int]) -> bool:
6        # Dictionary to store the first occurrence position (1-indexed) of each character
7        first_occurrence = defaultdict(int)
8      
9        # Iterate through the string with 1-indexed positions
10        for position, char in enumerate(s, 1):
11            # Convert character to its alphabetical index (0 for 'a', 1 for 'b', etc.)
12            char_index = ord(char) - ord('a')
13          
14            # If this character has appeared before
15            if first_occurrence[char_index]:
16                # Calculate the actual distance between two occurrences
17                # Distance = current_position - first_position - 1
18                actual_distance = position - first_occurrence[char_index] - 1
19              
20                # Check if actual distance matches the expected distance
21                if actual_distance != distance[char_index]:
22                    return False
23            else:
24                # Store the first occurrence position of this character
25                first_occurrence[char_index] = position
26      
27        return True
28
1class Solution {
2    public boolean checkDistances(String s, int[] distance) {
3        // Array to store the first occurrence position (1-indexed) of each letter
4        int[] firstOccurrencePosition = new int[26];
5      
6        // Iterate through each character in the string
7        for (int i = 1; i <= s.length(); ++i) {
8            // Get the letter index (0-25 for 'a'-'z')
9            int letterIndex = s.charAt(i - 1) - 'a';
10          
11            // If this letter has appeared before
12            if (firstOccurrencePosition[letterIndex] > 0) {
13                // Calculate the distance between two occurrences
14                // Distance = current position - first position - 1
15                int actualDistance = i - firstOccurrencePosition[letterIndex] - 1;
16              
17                // Check if the actual distance matches the required distance
18                if (actualDistance != distance[letterIndex]) {
19                    return false;
20                }
21            }
22          
23            // Store the current position as the first occurrence (1-indexed)
24            firstOccurrencePosition[letterIndex] = i;
25        }
26      
27        // All distances are valid
28        return true;
29    }
30}
31
1class Solution {
2public:
3    bool checkDistances(string s, vector<int>& distance) {
4        // Array to store the first occurrence position (1-indexed) of each letter
5        // Initialize all elements to 0 (indicating letter not yet seen)
6        int firstPosition[26]{};
7      
8        // Iterate through the string with 1-indexed position
9        for (int currentPos = 1; currentPos <= s.size(); ++currentPos) {
10            // Get the character index (0-25 for 'a'-'z')
11            int charIndex = s[currentPos - 1] - 'a';
12          
13            // If we've seen this character before
14            if (firstPosition[charIndex] > 0) {
15                // Calculate the distance between two occurrences
16                // Distance = current position - first position - 1
17                int actualDistance = currentPos - firstPosition[charIndex] - 1;
18              
19                // Check if the actual distance matches the required distance
20                if (actualDistance != distance[charIndex]) {
21                    return false;
22                }
23            }
24          
25            // Store the first occurrence position of this character
26            firstPosition[charIndex] = currentPos;
27        }
28      
29        return true;
30    }
31};
32
1/**
2 * Checks if the distance between each pair of same letters in string s
3 * matches the expected distance array
4 * @param s - Input string containing lowercase letters
5 * @param distance - Array where distance[i] represents the expected distance
6 *                   between two occurrences of the i-th letter of the alphabet
7 * @returns true if all letter distances match the expected values, false otherwise
8 */
9function checkDistances(s: string, distance: number[]): boolean {
10    // Array to store the first occurrence position (1-indexed) of each letter
11    // Index represents letter (0='a', 1='b', ..., 25='z')
12    const firstOccurrencePosition: number[] = Array(26).fill(0);
13  
14    // Get the length of the input string
15    const stringLength: number = s.length;
16  
17    // Iterate through each character in the string (using 1-based indexing)
18    for (let currentPosition: number = 1; currentPosition <= stringLength; ++currentPosition) {
19        // Convert current character to alphabet index (0-25)
20        // 'a' has ASCII code 97, so subtract 97 to get 0-based index
21        const alphabetIndex: number = s.charCodeAt(currentPosition - 1) - 97;
22      
23        // If this letter has been seen before
24        if (firstOccurrencePosition[alphabetIndex]) {
25            // Calculate actual distance between two occurrences
26            // Distance = current position - first position - 1
27            const actualDistance: number = currentPosition - firstOccurrencePosition[alphabetIndex] - 1;
28          
29            // Check if actual distance matches expected distance
30            if (actualDistance !== distance[alphabetIndex]) {
31                return false;
32            }
33        }
34      
35        // Store the current position as the first occurrence for this letter
36        firstOccurrencePosition[alphabetIndex] = currentPosition;
37    }
38  
39    // All distances match the expected values
40    return true;
41}
42

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. The algorithm iterates through the string exactly once using the enumerate function, and for each character, it performs constant-time operations: calculating the character index j, checking if the character has been seen before, comparing distances, and updating the dictionary.

The space complexity is O(|Σ|), where Σ is the character set (lowercase English letters in this case). The dictionary d can store at most 26 entries, one for each lowercase letter from 'a' to 'z', regardless of the input string length. Since the size of the alphabet is constant (26), this can also be expressed as O(1) space complexity.

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

Common Pitfalls

1. Incorrect Distance Calculation (Off-by-One Error)

The most common mistake is incorrectly calculating the distance between two occurrences of a letter. Many developers confuse:

  • The number of positions between two indices
  • The difference between two indices
  • Whether to use 0-indexed or 1-indexed positions

Incorrect implementations:

# Pitfall 1: Forgetting to subtract 1
actual_distance = position2 - position1  # Wrong! This includes both positions

# Pitfall 2: Using 0-indexed positions incorrectly
for i, char in enumerate(s):  # i is 0-indexed
    if first_occurrence[char_index] is not None:
        actual_distance = i - first_occurrence[char_index] - 1  # Wrong calculation!

Why this happens: When positions are 0-indexed, if 'a' appears at positions 0 and 3, the difference is 3, but there are only 2 letters between them (positions 1 and 2).

Solution: Use consistent indexing throughout. The provided solution uses 1-indexed positions with enumerate(s, 1), making the calculation intuitive: distance = current_pos - first_pos - 1.

2. Using Wrong Default Values in Dictionary

Incorrect implementation:

first_occurrence = {}  # or dict()

for position, char in enumerate(s, 1):
    char_index = ord(char) - ord('a')
  
    if char_index in first_occurrence:  # Need to check existence
        # calculate distance
    else:
        first_occurrence[char_index] = position

Problem: Using a regular dictionary requires explicit checking with in operator. If you use if first_occurrence[char_index]: with a regular dict, it will raise a KeyError.

Solution: Use defaultdict(int) which returns 0 for missing keys. Since positions start from 1, a value of 0 clearly indicates "not seen yet", making the code cleaner and avoiding the need for existence checks.

3. Confusing Character Index with Position

Incorrect implementation:

for i, char in enumerate(s):
    if first_occurrence[char]:  # Using character as key instead of index
        actual_distance = i - first_occurrence[char] - 1

Problem: Mixing up the character itself with its alphabetical index leads to incorrect dictionary keys and wrong distance array lookups.

Solution: Always convert the character to its alphabetical index: char_index = ord(char) - ord('a') and use this consistently for both dictionary access and distance array indexing.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

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

Load More