【LeetCode】30. Substring with Concatenation of All Words 單詞匹配子串

一、概述

輸入一個字符串s和一個字符串數組words,words中的字符串等長,若s有一個子串,由words中所有元素構成,則輸出這個字串的第一個字符的下標。輸出所有該類型子串的下標。

要求有點繁瑣,舉例子就很簡單:

s = "barfoothefoobarman",
words = ["foo","bar"]

那麼子串有barfoo,下標爲0;foobar,下標爲9。解答起來也很麻煩。

二、分析

我的代碼時空複雜度還不錯,因此就只分析我自己的了。

首先注意到words中元素的限制條件——長度相同,設置其爲l,那麼就可以根據這一點來進行遍歷:

第一輪:從s的第0個開始,每l個一組,與words中的元素比較;

第二輪:從s的第1個開始;第三輪,從s的第2個開始;...第l輪:從s的第l-1個開始。

然後就結束了。從第l個開始等價於從第0個開始。因此時間複雜度爲O(l*n),n爲s的長度。

然後要注意到words中的元素可以有重複的,最開始我就是沒注意到這點,用set去做,發現了之後所有代碼都要推倒從來,很是蛋疼。

對於每輪比較,我們維護一個hash表m,儲存當前words中還沒匹配的元素;維護一個指針tmp_ans,指向當前子串的頭部;維護一個整數now_len,表示當前子串的長度。當比較的時候,會出現三種情況:

①、s中的該子串在words中沒有對應的。那麼頭指針將指向該子串的下一個。now_len置零。m回覆初始狀態。

②、s中的該子串在word中有對應的,但是words中該子串對應的元素已經全部匹配完。那麼開始循環,頭指針對應的子串對應的m中的值+1,頭指針前移,長度減一;直到words中該子串對應的元素重新出現。

③、s中的該子串在word中有對應的,now_len與words的元素個數相等。那麼我們找到一個解。將這個解保存下來,頭指針前移;m增加,now_len減少。

代碼如下:

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if(s==""||words.size()==0)
            return res;
        unordered_map<string,int> m;
        for(int i=0;i<words.size();++i)
            if(words[i].size()>s.size())
                return res;
            else
                ++m[words[i]];
        for(int i=0;i<words[0].size();++i)
        {
            int tmp_ans=i;
            int now_len=0;
            for(int j=i;j<=s.size()-words[0].size();j+=words[0].size())
            {
                if(m.find(s.substr(j,words[0].size()))==m.end())
                {
                    while(tmp_ans!=j)
                    {
                        ++m[s.substr(tmp_ans,words[0].size())];
                        tmp_ans+=words[0].size();
                    }
                    tmp_ans+=words[0].size();
                    now_len=0;
                } 
                else
                {
                    --m[s.substr(j,words[0].size())];
                    ++now_len;
                    while(m[s.substr(j,words[0].size())]<0)
                    {
                        ++m[s.substr(tmp_ans,words[0].size())];
                        tmp_ans+=words[0].size();
                        --now_len;
                    }
                    if(now_len==words.size())
                    {
                        res.push_back(tmp_ans);
                        ++m[s.substr(tmp_ans,words[0].size())];
                        tmp_ans+=words[0].size();
                        --now_len;
                    }
                }
            }
            while(now_len>0)
            {
                ++m[s.substr(tmp_ans,words[0].size())];
                tmp_ans+=words[0].size();
                --now_len;
            }
        }
        return res;
    }
};

注意四點:

其一,當頭指針出現變化時,一定對應len和m的變化,不要忘了;

第二,每次j遍歷完,都要把m恢復原樣;

第三,用unorder_map而不是map,可以節省三倍時間;

第四,words中的元素長度如果比s大,可以直接返回。

三、總結

有些複雜的題目,主要就是words可以有重複元素,導致無法使用set,必須使用哈希表才行。這樣要維護多個變量和數據結構,就顯得有些麻煩。注意,只需要哈希表時不要用map,很拖後腿。

 

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