Leetcode 408. Valid Word Abbreviation

Problem Explanation

This problem requires us to implement a function, given a word and its potential abbreviations (consisting of letters and numbers only). The problem judges a valid word abbreviation based on the following rules:

  • The numbers in the abbreviation represent the count of consecutive characters to skip over in the word.
  • If the character in the abbreviation matches the character in the word, move to the next character for both strings.
  • The word is a valid abbreviation if, at the end, you can exhaust both the word and the abbreviation at the same time.

For instance, let's check the validity of the abbreviation "a2e" of the word "apple":

Here, according to the abbreviation "a2e", we skip 2 characters after "a", and then the next character should be "e". However, "e" does not match with "p" from "apple" after skipping 2 characters. Therefore, "a2e" is not a valid abbreviation for "apple".

The solution needs to identify and implement these rules. It needs to iterate over the characters of the word and the abbreviation to check whether the abbreviation correctly represents the word. This solution implementation does not require any specific algorithm but the usage of basic programming concepts like loop, condition checks, etc.

Solution in Python

Let's define the solution in Python. The solution uses two pointer variables i and j to iterate over the characters of the word and abbr respectively.

1
2python
3class Solution:
4    def validWordAbbreviation(self, word: str, abbr: str) -> bool:
5        i = 0  # word's index
6        j = 0  # abbr's index
7
8        while i < len(word) and j < len(abbr):
9            if word[i] == abbr[j]:
10                i += 1
11                j += 1
12                continue
13            if not abbr[j].isdigit() or abbr[j] == '0':
14                return False
15            num = 0
16            while j < len(abbr) and abbr[j].isdigit():  # keep adding the number as long as next character is a digit
17                num = num * 10 + int(abbr[j])
18                j += 1
19            i += num  # skip over the characters in word as represented by num in abbr
20
21        return i == len(word) and j == len(abbr)  # if both word and abbr are exhausted, return True

Solution in Java

This solution can be written in Java as follows.

1
2java
3class Solution {
4    public boolean validWordAbbreviation(String word, String abbr) {
5        int i = 0, j = 0;
6        while (i < word.length() && j < abbr.length()) {
7            if (word.charAt(i) == abbr.charAt(j)) {
8                i++;
9                j++;
10                continue;
11            } 
12            if (abbr.charAt(j) <= '0' || abbr.charAt(j) > '9')
13                return false;
14            int num = 0;
15            while (j < abbr.length() && Character.isDigit(abbr.charAt(j))) {
16                num = num * 10 + abbr.charAt(j) - '0';
17                j++;
18            }
19            i += num;
20        }
21        return i == word.length() && j == abbr.length();
22    }
23}

Solution in JavaScript

This solution can be applied to JavaScript as follows.

1
2javascript
3class Solution {
4    validWordAbbreviation(word, abbr) {
5        let i = 0, j = 0;
6        while (i < word.length && j < abbr.length) {
7            if (word.charAt(i) == abbr.charAt(j)) {
8                i++;
9                j++;
10                continue;
11            } 
12            if (abbr.charAt(j) <= '0' || abbr.charAt(j) > '9')
13                return false;
14            let num = 0;
15            while (j < abbr.length && !isNaN(abbr.charAt(j))) {
16                num = num * 10 + parseInt(abbr.charAt(j));
17                j++;
18            }
19            i += num;
20        }
21        return i == word.length && j == abbr.length;
22    }
23}

Solution in C++

This solution can be applied to C++ as follows.

1
2cpp
3class Solution {
4public:
5    bool validWordAbbreviation(const string& word, const string& abbr) {
6        int i = 0;  // word's index
7        int j = 0;  // abbr's index
8
9        while (i < word.length() && j < abbr.length()) {
10            if (word[i] == abbr[j]) {
11                ++i;
12                ++j;
13                continue;
14            }
15            if (abbr[j] <= '0' || abbr[j] > '9')
16                return false;
17            int num = 0;
18            while (j < abbr.length() && isdigit(abbr[j])) {
19                num = num * 10 + abbr[j] - '0';
20                ++j;
21            }
22            i += num;
23        }
24        return i == word.length() && j == abbr.length();
25    }
26};

Solution in C#

This solution can be applied to C# as follows.

1
2csharp
3public class Solution {
4    public bool ValidWordAbbreviation(string word, string abbr) {
5        int i = 0, j = 0;
6        while (i < word.Length && j < abbr.Length) {
7            if (word[i] == abbr[j]) {
8                i++;
9                j++;
10                continue;
11            } 
12            if (abbr[j] <= '0' || abbr[j] > '9')
13                return false;
14            int num = 0;
15            while (j < abbr.Length && Char.IsDigit(abbr[j])) {
16                num = num * 10 + abbr[j] - '0';
17                j++;
18            }
19            i += num;
20        }
21        return i == word.Length && j == abbr.Length;
22    }
23}

This solution works by the simple two-pointers approach where the pointers iterate through both the actual word and abbreviation. They check for valid characters and count values, and if any discrepancy or invalid structure is found, it immediately returns false. Else after complete iteration, it checks whether both the indices have reached their respective string's end, in which case it is a valid abbreviation.# Explanation and Understanding of the Solution

Our solution starts by initializing two pointers at 0 position for both the word and the abbreviation. We use these pointers to iterate over the characters of both the word and the abbreviation.

During each iteration, we compare the characters at the indexes pointed to by the two pointers:

  • If the characters match, we increment both pointers by one and continue to the next iteration.
  • If the character in the abbreviation is a digit, we enter into another loop to parse out the full number. The digit in the abbreviation can be a multi-digit number such as '10', '20' etc, so we use this inner loop to find out the full number. While we are finding out the full number, we also increment the abbreviation pointer to keep our position in sync.
  • If the character in the abbreviation is not a digit and doesn't match with the character at the same index in the word, we judge it as an invalid abbreviation and return false.

Any more digit characters in the abbreviation mean we have to skip that many characters in the original word, so we increment the word pointer by the number we parsed earlier.

As we are using two pointers to traverse through both strings, our solution will stop as soon as either pointer has reached the end of its string. In the very end, we check whether both pointers have reached the end of their respective strings. If they have, we return true, signifying the abbreviation is valid. If they haven't, we return false, indicating that the abbreviation is not valid per our rules.

One thing to notice is that we are using language-specific concepts in each implementation. In Python, we use string slicing to get the numbers from the abbreviation. In Java, we use the Character.isDigit() method to check if a character is a digit. Similarly, we have isNaN() function in JavaScript which returns true if a value is NaN (Not a Number), and 'isdigit' standard function in C++, and Char.IsDigit() in C#.

This type of problem is a nice example of sliding window or two pointer type problem and is frequently asked in technical interviews. Getting comfortable with these concepts will be very useful to solve more complex problems in the future.

Generally, we can say that the time complexity of this approach is linear (O(n)), as in the worst-case scenario, we might need to traverse through all characters in both strings. And the space complexity is constant (O(1)) as we are not using any extra space that scales with the input size. This algorithm is efficient and should work well even for large strings.

To further optimize this problem, we can modify our approach to stop as soon as we find any discrepancy rather than checking the characters of the entire word and abbreviation, but in our case, our solution should already work nicely as per our requirements.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫