14. 子串問題


字串問題有個(gè)通用的滑動(dòng)窗口算法,時(shí)間復(fù)雜度O(n2)
其中關(guān)鍵:
  1. 窗口大小不固定:構(gòu)造合適的count來控制窗口的滑動(dòng)与倡。
  2. 窗口大小固定:使用left滩愁、right控制窗口移動(dòng)虫给。
  3. 使用HashTable控制記錄字符出現(xiàn)情況藤抡。(不在t中字符,或者已出現(xiàn)的應(yīng)該<= 0抹估,在t中的字符應(yīng)該>=0)
  4. 若存在待比較字符串t杰捂,則初始化HashTable(t中字符對(duì)應(yīng)value大于0),同時(shí)right++,HashTable中value--棋蚌,count代表未命中字符個(gè)數(shù)嫁佳。若無,則HashTable全部為0谷暮,同時(shí)right++蒿往,HashTable中value++,count++湿弦,count代表不同字符個(gè)數(shù)或者重復(fù)字符個(gè)數(shù)瓤漏。
int findSubstring(string s){
        vector<int> map(128,0);
        int counter; // check whether the substring is valid
        int begin=0, end=0; //two pointers, one point to tail and one  head
        int d; //the length of substring

        for() { /* initialize the hash map here */ }

        while(end<s.size()){

            if(map[s[end++]]-- ?){  /* modify counter here */ }

            while(/* counter condition */){ 
                 
                 /* update d here if finding minimum*/

                //increase begin to make it invalid/valid again
                
                if(map[s[begin++]]++ ?){ /*modify counter here*/ }
            }  

            /* update d here if finding maximum*/
        }
        return d;
  }

相關(guān)問題,76/242/438/159/3/30/340/395/467

76. Minimum Window Substring
解析: 2段字符串颊埃,找出包含t的最小窗口
邊界:s比t小
思路:count代表未命中的字符蔬充,移動(dòng)right和left,找出count為0時(shí)班利,最小的窗口饥漫。時(shí)間復(fù)雜度O(nm)
// 10行的滑動(dòng)窗口代碼模板
    string minWindow(string s, string t) {
        vector<int> map(128,0);
        for(auto c: t) map[c]++;
        int counter=t.size(), begin=0, end=0, d=INT_MAX, head=0;
        while(end<s.size()){
            if(map[s[end++]]-->0) counter--; //in t
            while(counter==0){ //valid
                if(end-begin<d)  d=end-(head=begin);
                if(map[s[begin++]]++==0) counter++;  //make it invalid
            }  
        }
        return d==INT_MAX? "":s.substr(head, d);
    }

242. Valid Anagram
解析: 2段字符串,它們的內(nèi)容全部相同罗标,只是順序不同
邊界:長度不一致庸队,包含char個(gè)數(shù)不同
思路:使用排序O(nlog(n)),使用HashTable O(n)
// C++ map利用滑動(dòng)窗口思想
class Solution242 {
public:
    bool isAnagram(string s, string t) {
        if (s.size() != t.size()) {
            return false;
        }
        if (s == t) {
            return true;
        }
        
        vector<int> hash_map(128,0);
        for (auto c : t) {
            ++hash_map[c];
        }
        
        int count = t.size(), left = 0, right = 0;
        while (right < s.size()) {
            if(--hash_map[s[right++]] >= 0) {
                --count;
            }
            if(count == 0) {
                return true;
            }
        }
        
        return false;
    }
};

438. Find All Anagrams in a String
解析: 242的升級(jí)版。2段字符串闯割,找出字符串A中所有可能是與字符串B構(gòu)成回文結(jié)構(gòu)的下標(biāo)彻消。設(shè)s長度為n,p長度為m
邊界:字符串大小為空,字符串A的大小 < 字符串B的大小
誤區(qū):繼續(xù)按照242的套路宙拉,時(shí)間復(fù)雜度最小也是O(nm)
思路:使用滑動(dòng)窗口宾尚,窗口大小為m,滑動(dòng)n次谢澈,時(shí)間復(fù)雜度為O(n)煌贴。hash_map:使用p中字符出現(xiàn)次數(shù) 初始化該map。count:只會(huì)對(duì)hash中字符未命中的個(gè)數(shù)澳化;每次遍歷right++崔步,對(duì)應(yīng)的hash--;如果窗口大于hash缎谷,left++,對(duì)應(yīng)hash++×辛郑可以看出不在hash中的字符對(duì)應(yīng)的hash小于等于0(不會(huì)影響count)瑞你,而在hash中的字符對(duì)應(yīng)的hash開始就大于0。所以實(shí)際影響count的就是(left,right)區(qū)間內(nèi)的字符希痴,right經(jīng)過但left未經(jīng)過者甲,這些字符全部減-1,若此時(shí)hash>0的個(gè)數(shù)代表著初始化的hash正好被right移動(dòng)時(shí)減掉了砌创。
簡單解釋:利用了滑動(dòng)窗口的特性---右邊經(jīng)過的虏缸,左邊也會(huì)經(jīng)過。所以嫩实,右借左還刽辙,不會(huì)影響初始的結(jié)束條件。
時(shí)間復(fù)雜度O(n)
//
class Solution438 {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> result;
        vector<int> hash_map(128,0);
        for (auto c : p) {
            ++hash_map[c];
        }
        
        int count = p.size(), left = 0, right = 0;
        while (right < s.size()) {
            if(--hash_map[right++] >= 0) {
                --count;
            }
            if(count == 0) {
                result.push_back(left);
            }
            if((right - left) == p.size() && ++hash_map[left++] > 0) {
                ++count;
            }
        }
        
        return result;
    }
}

159. Longest Substring with At Most Two Distinct Characters
解析: 找出最多包含2個(gè)字符的最長子串甲献。
邊界:字符串長度小于3時(shí)宰缤,直接返回該字符串長度。
思路:count此處記錄不同的字符個(gè)數(shù)晃洒,count > 2時(shí)慨灭,為invalid的情況。
時(shí)間復(fù)雜度O(n2)
int lengthOfLongestSubstringTwoDistinct(string s) {
    vector<int> hash_map(128,0);
    int counter = 0, left = 0, right = 0, d = 0;
    while (right < s.size()) {
        if (hash_map[s[right++]]++ == 0) {
            ++counter;
        }
        while(counter > 2) {
            if(--hash_map[s[left++]] == 0) {
                --counter;
            }
        }
        d = max(d, right - left);
    }
    return d;
}

3. Longest Substring Without Repeating Characters
解析: 找出字符不重復(fù)的最長子串球及。
邊界:字符串長度小于2時(shí)氧骤,直接返回該字符串長度。
思路:count此處記錄重復(fù)字符個(gè)數(shù)吃引,count為1時(shí)语淘,為invalid的情況,需要移動(dòng)left际歼。
時(shí)間復(fù)雜度O(n2)
int lengthOfLongestSubstring(string s) {
    if (s.size() < 2) {
        return s.size();
    }
    vector<int> hash_map(128, 0);
    int count = 0, left = 0, right = 0, d = 0;
    while (right < s.size()) {
        if (hash_map[s[right++]]++ == 1) {
            ++count;
        }
        while (count == 1) {
            if (--hash_map[s[left++]] == 1) {
                --count;
            }
        }
        d = max(d, right - left);
    }
    return d;
};
最優(yōu)算法的時(shí)間復(fù)雜度為O(n)惶翻,使用HashTable記錄字符最靠右的位置
int lengthOfLongestSubstring(string s) {
    if(s.size() < 2) {
        return s.size();
    }
    
    vector<int> hash_map(256,-1);
    int left = 0, right = 0, d = 0;
    while (right < s.size()) {
        if (hash_map[s[right]] >= left) {
            left = hash_map[s[right]] + 1;
        }
        hash_map[s[right]] = right++;
        d = max(d, right - left);
    }
    return d;
};

30. Substring with Concatenation of All Words
解析: 找出包含列表中全部的字符串的起始位置集合,類似438鹅心。
邊界:s的大小小于列表長度吕粗。
思路:滑動(dòng)窗口,left依次劃過每個(gè)字符旭愧,判讀left-right之間的字符串是否全部與列表命中颅筋。
時(shí)間復(fù)雜度O(mn)
bool concatenation(string input, unordered_map<string, int> hash_table,int len) {
    int length = len;
    for (int i = 0; i < input.size()/len; ++i) {
        if (--hash_table[input.substr(i*length, length)] < 0) {
            return false;
        }
    }
    for (unordered_map<string, int>::iterator it = hash_table.begin(); it != hash_table.end(); ++it) {
        if (it->second != 0) {
            return false;
        }
    }
    return true;
}
vector<int> findSubstring(string s, vector<string>& words) {
    vector<int> result;
    if (words.size() == 0 || s.size() < (words[0].size())*words.size()) {
        return result;
    }

    int length = words[0].size();
    unordered_map<string,int> hash_table;
    for (string word : words) {
        hash_table[word]++;
    }

    for (int i = 0; i < s.size() - length + 1; ++i) {
        if (concatenation(s.substr(i, words.size()*length), hash_table, length)) {
            result.push_back(i);
        }
    }
    return result;
}

395. Longest Substring with At Least K Repeating Characters
解析: 找出每個(gè)字符最少重復(fù)K次的最長字符串。
邊界:子串長度小于K
思路:將字符串按字符詞典中小于K進(jìn)行劃分输枯,若字符詞典中出現(xiàn)次數(shù)全部大于K议泵,則返回該字符串長度,并遞歸執(zhí)行桃熄。
時(shí)間復(fù)雜度O(nlogn)
int longestSubstring(string s, int k) {
    if (s.size() < k) {
        return 0;
    }
    unordered_map<char,int> hash_map;
    for (char c : s) {
        hash_map[c]++;
    }

    set<char> splits;
    for (unordered_map<char, int>::iterator it = hash_map.begin(); it != hash_map.end(); ++it) {
        if (it ->second < k) {
            splits.insert(it->first);
        }
    }
    if (splits.size() == 0) {
        return s.size();
    }
    else if (splits.size() == hash_map.size()) {
        return 0;
    }
    vector<int> splits_loc;
    for (int i = 0; i < s.size(); ++i) {
        if (splits.find(s[i]) != splits.end()) {
            splits_loc.push_back(i);
        }
    }

    
    vector<int> result;
    int left = 0, right = -1;
    for (int loc : splits_loc) {
        left = right + 1;
        right = loc;
        result.push_back(longestSubstring(s.substr(left,right-left),k));
    }
    result.push_back(longestSubstring(s.substr(left+1, s.size() - 1 - left), k));

    return *(std::max_element(std::begin(result),std::end(result)));
}


467. Unique Substrings in Wraparound String
解析: 連續(xù)字符的最大組合個(gè)數(shù)
邊界:不連續(xù)是組合個(gè)數(shù)為單個(gè)的
思路:有n個(gè)連續(xù)的就有 (1+2+3+...+n)種組合情況先口,額外加入不連續(xù)的字符個(gè)數(shù)即可。同時(shí)要考慮,兩段連續(xù)字符可能有部分重合的情況(這時(shí)候選取長的)碉京。
時(shí)間復(fù)雜度O(nlogn)
int findSubstringInWraproundString(string p) {
    vector<int> letters(26,0);
    int res = 0, len = 0;
    for (int i = 0; i < p.size(); ++i) {
        int cur = p[i] - 'a';
        if (i > 0 && p[i-1] != (cur + 26 - 1) % 26 + 'a') {
            len = 0;
        }
        if(++len > letters[cur]) {
            res += len - letters[cur];
            letters[cur] = len;
        }
    }
    return res;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厢汹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谐宙,更是在濱河造成了極大的恐慌烫葬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凡蜻,死亡現(xiàn)場(chǎng)離奇詭異搭综,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)划栓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門兑巾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茅姜,你說我怎么就攤上這事闪朱。” “怎么了钻洒?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵奋姿,是天一觀的道長。 經(jīng)常有香客問我素标,道長称诗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任头遭,我火速辦了婚禮寓免,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘计维。我一直安慰自己袜香,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布鲫惶。 她就那樣靜靜地躺著蜈首,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欠母。 梳的紋絲不亂的頭發(fā)上欢策,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音赏淌,去河邊找鬼踩寇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛六水,可吹牛的內(nèi)容都是我干的俺孙。 我是一名探鬼主播辣卒,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鼠冕!你這毒婦竟也來了添寺?” 一聲冷哼從身側(cè)響起胯盯,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤懈费,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后博脑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憎乙,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年叉趣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泞边。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疗杉,死狀恐怖阵谚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烟具,我是刑警寧澤梢什,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站朝聋,受9級(jí)特大地震影響嗡午,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冀痕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一荔睹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧言蛇,春花似錦僻他、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至跟伏,卻和暖如春丢胚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背受扳。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工携龟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勘高。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓峡蟋,卻偏偏與公主長得像坟桅,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蕊蝗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗(yàn)仅乓。 張土汪:刷leetcod...
    土汪閱讀 12,745評(píng)論 0 33
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法蓬戚,內(nèi)部類的語法夸楣,繼承相關(guān)的語法,異常的語法子漩,線程的語...
    子非魚_t_閱讀 31,632評(píng)論 18 399
  • 教你如何迅速秒殺掉:99%的海量數(shù)據(jù)處理面試題 本文經(jīng)過大量細(xì)致的優(yōu)化后豫喧,收錄于我的新書《編程之法》第六章中,新書...
    Helen_Cat閱讀 7,421評(píng)論 1 39
  • (一)《好好學(xué)習(xí)》閱讀感想 陷入學(xué)習(xí)的老鼠賽道與《富爸爸窮爸爸》里面提及的現(xiàn)金流游戲老鼠賽跑幢泼,都跳不出老鼠圈紧显,原地...
    走向陽光的自己閱讀 587評(píng)論 0 4
  • 生產(chǎn)基地就在無錫鴻山物聯(lián)網(wǎng)小鎮(zhèn)的摩拜單車終于入錫了! 加入摩拜 之前早就下載了摩拜單車App缕棵,那就趕緊注冊(cè)吧孵班,手機(jī)...
    自在牛閱讀 803評(píng)論 3 6