1. 程式人生 > >[LeetCode] Group Shifted Strings 群組偏移字串

[LeetCode] Group Shifted Strings 群組偏移字串

Given a string, we can "shift" each of its letter to its successive letter, for example: "abc" -> "bcd". We can keep "shifting" which forms the sequence:

"abc" -> "bcd" -> ... -> "xyz"

Given a list of strings which contains only lowercase alphabets, group all strings that belong to the same shifting sequence.

For example, given: ["abc", "bcd", "acef", "xyz", "az", "ba", "a", "z"]
Return:

[
  ["abc","bcd","xyz"],
  ["az","ba"],
  ["acef"],
  ["a","z"]
]

Note: For the return value, each inner list's elements must follow the lexicographic order.

這道題讓我們重組偏移字串,所謂偏移字串,就是一個字串的每個字元按照字母順序表偏移相同量得到的另一個字串,兩者互為偏移字串,注意相同字串是偏移字串的一種特殊情況,因為偏移量為0。現在給了我們一堆長度不同的字串,讓我們把互為偏移字串的歸併到一起,我最開始想的是建立字元度和該長度的所有偏移字串的對映,但是很明顯的錯誤是相同長度的不一定都是偏移字串,比如'ab'和'ba‘,所以只能用雜湊表來建立一個字串和所有和此字串是偏移字串的集合之間的對映,由於題目要求結果是按字母順序的,所以用multiset來儲存結果,一來可以儲存重複字串,二來可以自動排序。然後我還寫了一個判斷二個字串是否互為偏移字串的函式,注意在比較兩個字母距離時採用了加26,再對26取餘的trick。我們遍歷給定字串集,對於遍歷到的字串,我們再遍歷雜湊表,和每個關鍵字呼叫isShifted函式來比較,如果互為偏移字串,則加入其對應的字串集,並標記flag,最後遍歷完雜湊表,沒有跟任何關鍵字互為偏移,那麼就新建一個對映,最後要做的就是把multiset轉換為vector即可,參見程式碼如下:

解法一:

// Correct but complicated
class Solution {
public:
    vector<vector<string>> groupStrings(vector<string>& strings) {
        vector<vector<string> > res;
        unordered_map<string, multiset<string>> m;
        for (auto a : strings) {
            
bool b = false; for (auto it = m.begin(); it != m.end(); ++it) { if (isShifted(it->first, a)) { it->second.insert(a); b = true; } } if (!b) m[a] = {a}; } for (auto it = m.begin(); it != m.end(); ++it) { res.push_back(vector<string>(it->second.begin(), it->second.end())); } return res; } bool isShifted(string s1, string s2) { if (s1.size() != s2.size()) return false; int diff = (s1[0] + 26 - s2[0]) % 26; for (int i = 1; i < s1.size(); ++i) { if ((s1[i] + 26 - s2[i]) % 26 != diff) return false; } return true; } };

上面那個方法挺複雜的,其實有更好的方法,網友的智慧無窮啊,上面那個方法的不高效之處在於對於每個遍歷到的字串,都要和雜湊表中所有的關鍵字都比較一次,而其實我們可以更加巧妙的利用偏移字串的特點,那就是字串的每個字母和首字元的相對距離都是相等的,比如abc和efg互為偏移,對於abc來說,b和a的距離是1,c和a的距離是2,對於efg來說,f和e的距離是1,g和e的距離是2。再來看一個例子,az和yx,z和a的距離是25,x和y的距離也是25(直接相減是-1,這就是要加26然後取餘的原因),那麼這樣的話,所有互為偏移的字串都有個unique的距離差,我們根據這個來建立對映就可以很好的進行單詞分組了,這個思路真實太讚了,參見程式碼如下:

解法二:

class Solution {
public:
    vector<vector<string>> groupStrings(vector<string>& strings) {
        vector<vector<string> > res;
        unordered_map<string, multiset<string>> m;
        for (auto a : strings) {
            string t = "";
            for (char c : a) {
                t += to_string((c + 26 - a[0]) % 26) + ",";
            }
            m[t].insert(a);
        }
        for (auto it = m.begin(); it != m.end(); ++it) {
            res.push_back(vector<string>(it->second.begin(), it->second.end()));
        }
        return res;
    }
};

參考資料: