LeetCode 面试经典150题 [31/150 串联所有单词的子串]


avatar
GuoYulong 2024-06-24 121

题目描述

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd","cdabef", "cdefab","efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接
输出顺序无关紧要。返回 [9,0] 也是可以的

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接
所以我们返回一个空数组

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接

个人C++解答


内存爆炸:
考虑采用两个哈希表,滑动窗口一个,原words一个,每个窗口建立一个表,判断是否与原表相同,相同的话该子串有效,否则无效;

GYL
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        std::unordered_map<string, int> myMap;
        for (auto word : words) myMap[word] += 1;
        vector<int> result;
        int word_length = words[0].size();
        int word_num = words.size();
        if (s.size() < word_length * words.size())
            return result;
        int left = 0, right = word_length * word_num;
        std::unordered_map<string, int> myTemp;
        for (; right <= s.size(); left++, right++) {
            myTemp.clear();
            for (int t_l = 0; t_l < word_num; t_l++) {
                string temp_key = s.substr(left, right).substr(word_length * t_l, word_length);
                myTemp[temp_key] += 1;
            }
            if(areMapsEqual(myTemp,myMap)) result.push_back(left);
        }
        return result;
    }
    bool areMapsEqual(const std::unordered_map<string, int>& map1,
                      const std::unordered_map<string, int>& map2) {
        if (map1.size() != map2.size()) {
            return false;
        }
        for (const auto& pair : map1) {
            auto it = map2.find(pair.first);
            if (it == map2.end() || it->second != pair.second) {
                return false;
            }
        }
        return true;
    }
};


内存爆炸:
依次滑动单个窗口,拆分字符串,传入HashMap,遍历words,每个HashMap[word]-=1,判断HashMap的大小是否为0,为0的话说明拆分成的子串每个都在words中存在过且数量对应;

GYL
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string, int> myMap;
        vector<int> result;
        int word_length = words[0].size();
        int word_num = words.size();
        if (s.size() < word_length * words.size())
            return result;
        int left = 0, right = word_length * word_num,flag = 0;
        for (; right <= s.size(); left++, right++) {
            flag = 0;
            myMap.clear();
            for (int t_l = 0; t_l < word_num; t_l++) {
                string temp_key = s.substr(left, right).substr(word_length * t_l, word_length);
                myMap[temp_key] ++;
            }
            for(auto word:words){
                myMap[word]--;
                if(myMap[word]==0){
                    myMap.erase(word);
                }
            }
            if(myMap.size()==0)
                result.push_back(left);
        }
        return result;
    }
   
};


内存允许,时间爆炸:
优化代码,使得裁剪字符串和构建哈希表的次数变少,从而减少内存开销,时间复杂度没变

GYL
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> result;
        if (s.empty() || words.empty()) return result;
        if (s.size() < words[0].size() * words.size()) return result;
        int wordLen = words[0].size();
        int wordNum = words.size();
        unordered_map<string, int> wordCount;
        for (const string& word : words) {
            wordCount[word]++;
        }
        
        int totalLen = wordLen * wordNum;
        for (int left = 0; left <= s.size() - totalLen; left++) {
            unordered_map<string, int> matched(wordCount);
            for (int right = left; right < left + totalLen; right += wordLen) {
                string word = s.substr(right, wordLen);
                if (matched.find(word) == matched.end()) break;
                
                matched[word]--;
                if (matched[word] == 0) {
                    matched.erase(word);
                }
            }
            if (matched.empty()) {
                result.push_back(left);
            }
        }
        return result;
    }
};

GPT优化代码


核心优化:

for (int i = 0; i < wordLen; i++) {
            unordered_map<string, int> window;
            int left = i, right = i, count = 0;

从每个可能的起点(0 到 wordLen - 1)开始,确保所有可能的起点都能被覆盖。
时间复杂度降低到了 O(n * wordLen), 其中 n 是字符串 s 的长度,wordLen 是单词的长度

GPT
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> result;
        if (s.empty() || words.empty()) return result;

        int wordLen = words[0].size();
        int wordNum = words.size();
        int totalLen = wordLen * wordNum;

        if (s.size() < totalLen) return result;

        unordered_map<string, int> wordCount;
        for (const string& word : words) {
            wordCount[word]++;
        }

        // Iterate over each possible starting index within the first word length
        for (int i = 0; i < wordLen; i++) {
            unordered_map<string, int> window;
            int left = i, right = i, count = 0;

            while (right + wordLen <= s.size()) {
                string word = s.substr(right, wordLen);
                right += wordLen;
                if (wordCount.find(word) != wordCount.end()) {
                    window[word]++;
                    count++;
                    
                    // If the word count exceeds, slide the window from the left
                    while (window[word] > wordCount[word]) {
                        string leftWord = s.substr(left, wordLen);
                        window[leftWord]--;
                        left += wordLen;
                        count--;
                    }

                    // If count equals wordNum, a valid substring is found
                    if (count == wordNum) {
                        result.push_back(left);
                    }
                } else {
                    // Reset the window
                    window.clear();
                    count = 0;
                    left = right;
                }
            }
        }

        return result;
    }
};

相关阅读

注意!!!

新增会员中心页面,方便管理个人账户,充值功能暂不开启,请勿为本网站进行任何充值活动!!!

通知!!!

① 过年好!!!拖更几个月了已经,年后继续更新!!