Shortest Completing Word, by LeetCode

Problem is this: https://leetcode.com/problems/shortest-completing-word/description/:

Find the minimum length word from a given dictionary words, which has all the letters from the string licensePlate. Such a word is said to complete the given string licensePlate
Here, for letters we ignore case. For example, "P" on the licensePlate still matches "p" on the word.
It is guaranteed an answer exists. If there are multiple answers, return the one that occurs first in the array.
The license plate might have the same letter occurring multiple times. For example, given a licensePlate of "PP", the word "pair"does not complete the licensePlate, but the word "supper" does.
Example 1:
Input: licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"]
Output: "steps"
Explanation: The smallest length word that contains the letters "S", "P", "S", and "T".
Note that the answer is not "step", because the letter "s" must occur in the word twice.
Also note that we ignored case for the purposes of comparing whether a letter exists in the word.
Example 2:
Input: licensePlate = "1s3 456", words = ["looks", "pest", "stew", "show"]
Output: "pest"
Explanation: There are 3 smallest length words that contains the letters "s".
We return the one that occurred first.

Its difficulty is marked as "Medium" but upon close evaluation, the problem can be addressed with:

  • The use of a HashTable with the number of occurrences of each letter in the licence plate
  • Just be careful with the uppercase letters and non-letters
  • Use the "clone" function in C#, very handy to make a copy of a HashTable
  • In one pass check each word in order, and the moment that you find a candidate, break
Code is down below. Hugs and kisses! Marcelo

    public class Solution
    {
        public string ShortestCompletingWord(string licensePlate, string[] words)
        {
            Hashtable lp = new Hashtable();

            string lcLicensePlace = licensePlate.ToLower();
            for (int i = 0; i < lcLicensePlace.Length; i++)
            {
                if (lcLicensePlace[i] >= 'a' && lcLicensePlace[i] <= 'z')
                {
                    if (!lp.ContainsKey(lcLicensePlace[i])) lp.Add(lcLicensePlace[i], 0);
                    lp[lcLicensePlace[i]] = (int)lp[lcLicensePlace[i]] + 1;
                }
            }

            string minWord = "";
            int minLength = Int32.MaxValue;

            for (int i = 0; i < words.Length; i++)
            {
                Hashtable lpc = (Hashtable)lp.Clone();
                string lcWord = words[i].ToLower();
                for (int j = 0; j < lcWord.Length; j++)
                {
                    if (lpc.ContainsKey(lcWord[j]))
                    {
                        lpc[lcWord[j]] = (int)lpc[lcWord[j]] - 1;
                        if ((int)lpc[lcWord[j]] == 0) lpc.Remove(lcWord[j]);
                        if (lpc.Count == 0)
                        {
                            if (words[i].Length < minLength)
                            {
                                minLength = words[i].Length;
                                minWord = words[i];
                            }
                            break;
                        }
                    }
                }
            }

            return minWord;
        }
    }

Comments

  1. The problem is indeed fairly easy, but still fun to solve :) My solution is basically the same, but I have extracted a special "Fingerprint" entity that basically captures the essence of the string for the purposes of this problem and use an array of size 26 to back it up instead of using a hashtable.

    class Fingerprint {
    private:
    array counts;
    public:
    Fingerprint(const string& word): counts({}) {
    for (char ch : word) {
    if (ch >= 'a' && ch <= 'z') {
    counts[ch-'a'] += 1;
    } else if (ch >= 'A' && ch <= 'Z') {
    counts[ch-'A'] += 1;
    }
    }
    }

    bool is_covered_by(const Fingerprint& other) {
    for (int i = 0; i < counts.size(); ++i) {
    if (other.counts[i] < counts[i]) return false;
    }
    return true;
    }
    };

    class Solution {
    public:
    string shortestCompletingWord(string licensePlate, const vector& words) {
    Fingerprint license_plate_signature(licensePlate);
    int shortest_so_far = -1;
    for (int i = 0; i < words.size(); ++i) {
    const auto& word = words[i];
    Fingerprint candidate_signature(word);
    if (license_plate_signature.is_covered_by(candidate_signature)) {
    if (shortest_so_far == -1 || word.size() < words[shortest_so_far].size()) {
    shortest_so_far = i;
    }
    }
    }
    return words[shortest_so_far];
    }
    };

    Thanks for sharing this fun little problem!
    Taras

    ReplyDelete

Post a Comment

Popular posts from this blog

Changing the root of a binary tree

ProjectEuler Problem 719 (some hints, but no spoilers)

The Power Sum, a recursive problem by HackerRank