[LeetCode] Letter Case Permutation 字母大小寫全排列


 

Given a string S, we can transform every letter individually to be lowercase or uppercase to create another string.  Return a list of all possible strings we could create.

Examples:
Input: S = "a1b2"
Output: ["a1b2", "a1B2", "A1b2", "A1B2"]

Input: S = "3z4"
Output: ["3z4", "3Z4"]

Input: S = "12345"
Output: ["12345"]

Note:

  • S will be a string with length at most 12.
  • S will consist only of letters or digits.

 

這道題給了我們一個只包含字母和數字的字符串,讓我們將字母以大小寫進行全排列,給的例子很好的說明了題意。博主認為這道題給Easy有點不合適,至少應該是Medium的水准。這題主要參考了官方解答貼的解法,我們關心的是字母,數字的處理很簡單,直接加上就可以了。比如說S = "abc",那么先讓 res = [""],然后res中的每個字符串分別加上第一個字符a和A,得到 ["a", "A"],然后res中的每個字符串分別加上第二個字符b和B,得到 ["ab", "Ab", "aB", "AB"],然后res中的每個字符串分別加上第三個字符c和C,得到 ["abc", "Abc", "aBc", "ABc", "abC", "AbC", "aBC", "ABC"],參見代碼如下:

 

解法一:

class Solution {
public:
    vector<string> letterCasePermutation(string S) {
        vector<string> res{""};
        for (char c : S) {
            int len = res.size();
            if (c >= '0' && c <= '9') {
                for (string &str : res) str.push_back(c);
            } else {
                for (int i = 0; i < len; ++i) {
                    res.push_back(res[i]);
                    res[i].push_back(tolower(c));
                    res[len + i].push_back(toupper(c));
                }
            }
        }
        return res;
    }
};

 

下面這種解法跟上面的解法沒有太大的區別,只不過沒有用到tolower()和toupper()這兩個built-in函數,而是使用了一個trick來flip大小寫字母,通過亦或32實現,為什么呢?因為我們知道 'A' = 65, 'B' = 66, 和 'a' = 97, 'b' = 98, 小寫字母的ASCII碼比大寫字母多32,剛好是(1 << 5),那么我們只要flip第五位上的1,就可以實現大小寫的交替了,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<string> letterCasePermutation(string S) {
        vector<string> res{""};
        for (char c : S) {
            int len = res.size();
            if (c >= '0' && c <= '9') {
                for (string &str : res) str.push_back(c);
            } else {
                for (int i = 0; i < len; ++i) {
                    res.push_back(res[i]);
                    res[i].push_back(c);
                    res[len + i].push_back(c ^ 32);
                }
            }
        }
        return res;
    }
};

 

我們再來看一種遞歸的寫法,是一種回溯的思路,比如說S = "abc",用一個pos指向當前處理的位置,初始化帶入0,然后再遞歸函數中,如果pos等於s的長度了,那么將s存入結果res再返回;否則調用遞歸函數,此時帶入pos+1,那么遞歸函數就會一直深入,直到pos等於s的長度了,那么此時就把"abc"存入結果res了,返回后此時pos=2,發現s[pos]是字母,則用上面解法中的flip方法來翻轉字母,並且調用遞歸函數,這樣"abC"就會存入結果res中,然后回溯到pos=1的位置,s[pos]是字符,可以flip,並且調用遞歸函數,這樣"aBC"就會存入結果res中,然后pos繼續往后遍歷,這樣"aBc"就會存入結果res中,然后回溯到pos=0的位置,s[pos]是字符,可以flip,並且調用遞歸函數,這樣"ABc"就會存入結果res中,然后繼續回溯,這樣"ABC"就會存入結果res中,pos又回溯到1的位置,s[pos]是字符,可以flip,並且調用遞歸函數,這樣"AbC"就會存入結果res中,然后pos繼續往后遍歷,這樣"Abc"就會存入結果res中,整個的順序為:[abc abC aBC aBc ABc ABC AbC Abc],參見代碼如下:

 

解法三:

class Solution {
public:
    vector<string> letterCasePermutation(string S) {
        vector<string> res;
        helper(S, 0, res);
        return res;
    }
    void helper(string& s, int pos, vector<string>& res) {
        if (pos == s.size()) {
            res.push_back(s);
            return;
        }
        helper(s, pos + 1, res);
        if (s[pos] > '9') {
            s[pos] ^= 32;
            helper(s, pos + 1, res);
        }
    }
};

 

下面這種解法是官方解答貼的Binary Mask解法,感覺也很巧妙,如果我們仔細觀察S = "abc"這個例子,結果會返回8個,為什么是8個呢,因為每個字母都有大小寫兩種可能,那么三個字母就有2x2x2=8,正好是2的三次方,那么不正好和二進制數相對應么,如果1對應大寫字母,0對應小寫字母,則有:

000 -> ABC
001 -> ABc
010 -> AbC
011 -> Abc
100 -> aBC
101 -> aBc
110 -> abC
111 -> abc

這樣的話,我們只需要先統計出S中字母的個數cnt,然后遍歷 [0, 2^cnt) 中的每個數字,對於每個數字,再遍歷S中的每個字符,如果是字母,那么如果當前位是1,則加入小寫字母,反之加入大寫字母;如果是數字,則直接加入即可。我們用j指向位,每次處理完一位后j自增1,表示對下一位進行處理,參見代碼如下:

 

解法四:

class Solution {
public:
    vector<string> letterCasePermutation(string S) {
        vector<string> res;
        int cnt = 0;
        for (char c : S) {
            if (c > '9') ++cnt;
        }
        for (int i = 0; i < (1 << cnt); ++i) {
            int j = 0;
            string word = "";
            for (char c : S) {
                if (c > '9') {
                    if (((i >> j++) & 1) == 1) {
                        word.push_back(tolower(c));
                    } else {
                        word.push_back(toupper(c));
                    }
                } else {
                    word.push_back(c);
                }
            }
            res.push_back(word);
        }
        return res;
    }
};

 

類似題目:

Subsets

 

參考資料:

https://leetcode.com/problems/letter-case-permutation/solution/

https://leetcode.com/problems/letter-case-permutation/discuss/115515/C++-backtrack-solution-w-trick

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM