力扣(LeetCode)30. 串联所有单词的子串(C++)

滑动窗口+哈希表

哈希表 t o t tot tot w o r d s words words 所有单词的出现次数。

维护滑动窗口,窗口长度 m × w m\times w m×w m m m 是单词数量 w w w是单词长度 , 窗口长度对应可行解的长度。哈希表 w d wd wd 维护滑动窗口内每个单词的出现次数。

维护有效串总数 c n t cnt cnt ,当 c n t = m cnt=m cnt=m 时,找到一个可行解。当右窗口右移,加入的单词是需要的, c n t + + cnt++ cnt++ , 当左窗口右移,移除的单词是需要的, c n t − − cnt-- cnt 。 对照 w d wd wd t o t tot tot 判断单词是否需要 。

提示 : 滑动窗口达到最大长度后,维护左窗口。

s s s 分成 w w w 组,起点从 0 0 0 w − 1 w-1 w1 ,遍历每一组,得到答案。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {//字符串哈希
        vector<int> ans;
        if(words.empty()) return ans;
        unordered_map<string,int> tot;
        for(auto &x:words) tot[x]++;
        int n = s.size(),m = words.size(),w = words[0].size();
        for(int i = 0;i<w;i++){
            int cnt = 0;
            unordered_map<string,int> wd;
            for(int j = i ;j<=n;j+=w){
                if(j >= i + m*w){//滑动窗口满了,维护左窗口
                    auto s1 = s.substr(j-m*w,w);
                    wd[s1]--;
                    if(tot[s1]>wd[s1]) cnt--;//减去了有效串
                }
                auto s2 = s.substr(j,w);
                wd[s2]++;
                if(tot[s2]>=wd[s2]) cnt++;//增加了有效串
                if(m == cnt) ans.push_back(j-(m-1)*w);
            }
        }
        return ans;
    }
};
  1. 时间复杂度 : O ( w × n ) O(w\times n) O(w×n) n n n 是字符串 s s s 的长度, w w w 是单词长度。遍历 w w w 组的时间复杂度 O ( w ) O(w) O(w) ,每组 n w \dfrac n w wn 个单词的时间复杂度 O ( n w ) O(\dfrac n w) O(wn) ,遍历字母得到单词的时间复杂度 O ( w ) O(w) O(w) ,三者是相乘关系,总时间复杂度 O ( w × n ) O(w\times n) O(w×n)
  2. 空间复杂度 : O ( w × m ) O(w\times m) O(w×m) m m m 是单词总数 w o r d s words words 的长度 , w w w 是单词长度。 哈希表 t o t tot tot 的空间复杂度是 O ( w × m ) O(w\times m) O(w×m) , 哈希表 t o t tot tot 的空间复杂度是 O ( w × m ) O(w\times m) O(w×m) ,总空间复杂度是 O ( 2 × w × m ) O(2\times w\times m) O(2×w×m) 。 忽略常数空间复杂度 O ( w × m ) O(w\times m) O(w×m)
博主致语

理解思路很重要!
欢迎读者在评论区留言,作为日更博主,看到就会回复的。

AC

力扣(LeetCode)30. 串联所有单词的子串(C++)_第1张图片

你可能感兴趣的:(墨染leetcode,leetcode,c++,算法,滑动窗口,哈希)