30.substring-with-concatenation-of-all-words
阿新 • • 發佈:2018-11-08
說實在的,這道題還是比較難的,因為沒有好的思路,其實從歷年各個公司的演算法真題來看,難題還是多出現在字串上,這道題就是比較靈活的一道,沒啥太好的思路,起初想著dfs,倒是可以做,但是開銷蠻大的,因此,這裡我參考大神的程式碼寫了使用hash來解決問題。達到O(N)的時間複雜度和O(N)的空間複雜度。
題目的大意是給定一個字串和一個字串對應的陣列,在字串中尋找一個字串,該字串正好包含字串陣列中所有的字串,如果字串陣列中有重複,對應的字元子串中也應該有幾個。舉了例子,字串陣列中如果是:{"word", "word"},對應的字串也應該是"wordword"才可以。另外題目中還有一個重要的條件,字串陣列中每個字串的長度是相等的,這對我們的後續工作很重要。返回符合條件的字串的起始位置座標。
因為字串陣列中每一個字串長度相等,假設長度為k,那麼我們第一次就可以檢測0~k-1,k~2k-1,……,第二次就可以檢測1~k, k+1~2k, ……, …… ,第k次就可以檢測k-1~2k-2,……,而每一次檢測,對應的步長都是k,字串總長度是N,兩層迴圈,一層是k,另一層是N/k,所以,時間複雜度就是O(N).
思路是這樣的,因為要尋找的字元子串,也就要求我們尋找的目標串連續的,那整個尋找的過程類似於滑動視窗。如果能理解是滑動視窗了,那接下來我們要面對的是兩種情況,
<1>,如果我們當前匹配的字串發生失配的情況,那一定要從當前位置重新開始計數。
<2>,如果字串出現某個單詞個數比原陣列中的多時,我們需要滑動視窗的左邊界向右,將多餘的單詞之前的字串都要捨棄。
以下是AC的程式碼:
class Solution { public: vector<int> findSubstring(string s, vector<string>& words) { vector<int> res; if (words.size()<1 || (s.size()<(words.size()*words[0].size()))) { return res; } int wordslen = words.size(), wordsize = words[0].size(); unordered_map<string, int> unmap1; for (string str : words) { ++unmap1[str]; } for (int i=0; i<wordsize; ++i) { unordered_map<string, int> unmap2; int left = i, count = 0; for (int j=i; j<=s.size()-wordsize; j+=wordsize) { if (unmap1.count(s.substr(j, wordsize))) { ++unmap2[s.substr(j, wordsize)]; ++count; while (unmap2[s.substr(j, wordsize)] > unmap1[s.substr(j, wordsize)]) { --unmap2[s.substr(left, wordsize)]; left += wordsize; --count; } } else { count = 0; left = j+wordsize; unmap2.clear(); } if (count == wordslen) { res.emplace_back(left); --unmap2[s.substr(left, wordsize)]; left += wordsize; --count; } } } return res; } };