1. 程式人生 > >[LeetCode] Shortest Completing Word 最短完整的單詞

[LeetCode] Shortest Completing Word 最短完整的單詞

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.

Note:

  1. licensePlate will be a string with length in range [1, 7].
  2. licensePlate
     will contain digits, spaces, or letters (uppercase or lowercase).
  3. words will have a length in the range [10, 1000].
  4. Every words[i] will consist of lowercase letters, and have length in range [1, 15].

這道題給了我們一個車牌號,還有一些單詞,讓我們找出包含這個車牌號中所有字母的第一個最短的單詞。車牌中的字母有大小寫之分,但是單詞只是由小寫單片語成的,所以需要把車牌號中的所有大寫字母都轉為小寫的,轉換方法很簡單,ASCII碼加上32即可。我們建立車牌中各個字母和其出現的次數之間的對映,同時記錄所有字母的個數total,然後遍歷所有的單詞,對於每個單詞都要單獨處理,我們遍歷單詞中所有的字母,如果其在車牌中也出現了,則對應字母的對映減1,同時還需匹配的字母數cnt也自減1,最後遍歷字母完成後,如果cnt為0(說明車牌中所有的字母都在單詞中出現了),並且結果res為空或長度大於當前單詞word的話,更新結果即可,參見程式碼如下:

解法一:

class Solution {
public:
    string shortestCompletingWord(string licensePlate, vector<string>& words) {
        string res = "";
        int total = 0;
        unordered_map<char, int> freq; 
        for (char c : licensePlate) {
            if (c >= 'a' && c <= 'z') {++freq[c]; ++total;}
            else if (c >= 'A' && c <= 'Z') {++freq[c + 32]; ++total;}
        }
        for (string word : words) {
            int cnt = total;
            unordered_map<char, int> t = freq;
            for (char c : word) {
                if (--t[c] >= 0) --cnt;
            }
            if (cnt == 0 && (res.empty() || res.size() > word.size())) {
                res = word;
            }
        }
        return res;
    }
}; 

如果這道題的單詞是按長度排序的話,那麼上面的方法就不是很高效了,因為其會強制遍歷完所有的單詞。所以我們考慮給單詞排序,博主這裡用了TreeMap這個資料結構建立單詞長度和包含所有該長度單詞的陣列之間的對映,其會自動按照單詞長度來排序。然後還使用了一個chars陣列來記錄車牌中的所有字母,這樣就可以方便的統計出字母總個數。我們從單詞長度等於字母總個數的對映開始遍歷,先檢驗該長度的所有單詞。這裡檢驗方法跟上面略有不同,但都大同小異,用一個bool型變數succ,初始化為true,然後建立一個字母和其出現次數的對映,先遍歷單詞,統計各個字母出現的次數。然後就遍歷chars陣列,如果chars中某個字母不在單詞中,那麼succ賦值為false,然後break掉。最後我們看succ,如果仍為true,直接返回當前單詞word,之後的單詞就不用再檢驗了,參見程式碼如下:

解法二:

class Solution {
public:
    string shortestCompletingWord(string licensePlate, vector<string>& words) {
        map<int, vector<string>> m;
        vector<char> chars;
        for (string word : words) {
            m[word.size()].push_back(word);
        }
        for (char c : licensePlate) {
            if (c >= 'a' && c <= 'z') chars.push_back(c);
            else if (c >= 'A' && c <= 'Z') chars.push_back(c + 32);
        }
        for (auto a : m) {
            if (a.first < chars.size()) continue;
            for (string word : a.second) {
                bool succ = true;
                unordered_map<char, int> freq;
                for (char c : word) ++freq[c];
                for (char c : chars) {
                    if (--freq[c] < 0) {succ = false; break;}
                }
                if (succ) return word;
            }
        }
        return "";
    }
};

參考資料: