Ransom Note

The problem description can be found in LeetCode.

Approach

Usually, for problems that involve strings, we might need to use a counter dictionary for the characters.

In order to build our ransom note we need a certain number of characters, and all of them must be in the magazine. So we have to count such characters and check if we have enough of them in the magazine.

Since our alphabet is limited to lowercase English letters, we can count them by using a static list of 26 elements as we did for the Valid Anagram problem.

Once we have the two counters, we can simply iterate over the one of the ransom note and check if the magazine counter has enough occurrences for the i-th character:

from typing import List


class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        if len(ransomNote) > len(magazine):
            return False

        rn_counter = self.get_chars_counter(ransomNote)
        m_counter = self.get_chars_counter(magazine)

        for i, occ in enumerate(rn_counter):
            if m_counter[i] < occ:
                return False

        return True

    def get_chars_counter(self, s: str) -> List[int]:
        m = [0] * 26

        for char in s:
            idx = ord(char) - ord("a")
            m[idx] += 1

        return m

This would take time and space, where is the length of the magazine string (which is greater than the size of the string ransomNote).

Even if this solution is accepted, there is a slightly better way to solve the issue.

In fact, for this kind of problems, sometimes we can build a single counter and then apply some logic to it to verify its properties. In this case, once we get the counter, we can simply iterate over the ransomNote string and subtract the i-th character to the counter. If the number of its occurrence is less than 0, it means that we do not have enough characters from the magazine, hence, we cannot build the ransom note.

Please, notice that in terms of notation, the complexity is the same compared to the previous approach. However, the code will be faster since we are going to run less instructions.

Solution

from typing import List


class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        if len(ransomNote) > len(magazine):
            return False

        counter = self.get_chars_counter(magazine)

        for char in ransomNote:
            idx = self.get_idx_from_char(char)
            counter[idx] -= 1

            if counter[idx] < 0:
                return False

        return True

    def get_chars_counter(self, s: str) -> List[int]:
        m = [0] * 26

        for char in s:
            idx = self.get_idx_from_char(char)
            m[idx] += 1

        return m

    def get_idx_from_char(self, char: str) -> int:
        assert isinstance(char, str)
        assert len(char) == 1

        return ord(char) - ord("a")

Complexity

  • Time:
  • Space:

Where is the length of the magazine string.