[LeetCode] 30. 串聯所有單詞的子串(哈希表)

30. 串聯所有單詞的子串

給定一個字符串 s 和一些長度相同的單詞 words。找出 s 中恰好可以由 words 中所有單詞串聯形成的子串的起始位置。

注意子串要與 words 中的單詞完全匹配,中間不能有其他字符,但不需要考慮 words 中單詞串聯的順序。

在這裏插入圖片描述
解題思路: 先將題意轉換一種說法,題目給定一組長度相同的單詞,假設個數爲n,長度爲len,在字符串s中找一段長度爲 n*len的子字符串,使得這個子字符串恰好由單詞words組成,那麼最自然的想法是,對字符串s每個長度爲n*len的子字符串都進行判斷,但是起點在len(s)-n*len之後的子字符串不用判斷,因爲長度不夠(提前剪枝,降低時間複雜度),那麼剩下的就是如何設計函數來判斷子字符串是否恰好能由words串聯而成,有兩種方法:

  1. 由於字符串會重複,可以將words存在multiset中,然後在子字符串中以步進len截取子字符串,判斷是否在multiset中,若存在則將multiset中這個字符串erase掉,否則,若不存在,則判斷multiset是否爲空了,爲空說明已經找到串聯的字符串;
  2. 或者不使用multiset而使用hashmap,對words詞頻進行統計,同樣以步進len截取子字符串,若子字符串存在於hashmap中,則將hashmap減1,當hashmap對應項爲0時,erase,若子字符串不存在於hashmap中,則判斷hashmap是否爲空,若爲空,則表明已經找到串聯的字符串。

最後提交OJ發現,方法1 TLE了,而方法2險過,看來方法1使用的rb-tree複雜度有點高,以後算法題中能用hash儘量用hash。不過方法2對應的整個算法,時間複雜度爲O(n2)O(n^2),雖然網上有時間複雜度爲O(n),但是憑現在的水平有點難以理解,還是本題算法好理解一點。

class Solution {
public:
    bool helper(string &s, int i, vector<string>& words) {
        int len = words[0].size();
        unordered_map<string, int> tmpcnt = wordsCnt;
        for (int j = i; j + len <= s.size(); j += len) {
            string tmp = s.substr(j, len);
            if (tmpcnt.count(tmp) == 0) {
                return tmpcnt.empty();
            }
            --tmpcnt[tmp];
            if (tmpcnt[tmp] == 0) tmpcnt.erase(tmp);
        }
        return tmpcnt.empty();
    }
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty() || words[0].empty())  return {};
        vector<int> res;
        for (auto &word : words) ++wordsCnt[word];
        int n = words.size(), len = words[0].size();
        for (int i = 0; i <= (int)s.size() - n * len; ++i) {
            if (helper(s, i, words)) {
                res.push_back(i);
            }
        }
        return res;
    }
private:
    unordered_map<string, int> wordsCnt;    
};

————————————

參考資料:

https://www.cnblogs.com/grandyang/p/4521224.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章