算法-串联所有单词的字串(30)解决超时问题

leetcode题目链接

这道题也是非常经典的滑动窗口,一般的思路就是创建一个unordered_map<string,int>wordcount用于存储words中每个单词出现的次数,然后创建一个滑动窗口,之后遍历字符串,如果当前窗口内的单词都在words中且次数出现正确,则把索引加入结果,以下是代码:

// 计算每个单词的长度
    int wordLen = words[0].length();
    // 计算所有单词的总长度
    int totalLen = wordLen * words.size();
    // 如果字符串长度小于所有单词的总长度,直接返回空
    if (s.length() < totalLen) {
        return {};
    }
    
    // 创建一个哈希表,存储 words 中每个单词出现的次数
    unordered_map<string, int> wordCount;
    for (const auto& word : words) {
        wordCount[word]++;
    }
    
    vector<int> result;
    // 遍历字符串 s,维护一个滑动窗口
    for (int i = 0; i <= s.length() - totalLen; i++) {
        // 复制一份 wordCount,用于当前窗口
        unordered_map<string, int> currentCount(wordCount);
        int j = 0;
        // 检查当前窗口中的单词是否都在 words 中,且出现次数正确
        for (; j < words.size(); j++) {
            string word = s.substr(i + j * wordLen, wordLen);
            if (currentCount.count(word) == 0 || currentCount[word] == 0) {
                break;
            }
            currentCount[word]--;
        }
        // 如果所有单词都匹配,则将起始索引加入结果
        if (j == words.size()) {
            result.push_back(i);
        }
    }
    
    return result;
}

这种情况在数据量小的情况下可以胜任,但是一旦数据量多了,就会超时,那我么怎么解决呢

其实我们要做的就是解决一下不必要的窗口操作

比如一旦出现一个不是words中的单词,则此窗口不符合,直接进行下一个窗口,解决不必要的操作

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty()) {
            return {};
        }
        
        // 计算每个单词的长度
        int wordLen = words[0].length();
        // 计算所有单词的总长度
        int totalLen = wordLen * words.size();
        // 如果字符串长度小于所有单词的总长度,直接返回空
        if (s.length() < totalLen) {
            return {};
        }
        
        // 创建一个哈希表,存储 words 中每个单词出现的次数
        unordered_map<string, int> wordCount;
        for (const auto& word : words) {
            wordCount[word]++;
        }
        
        vector<int> result;
        
        // 遍历每一个可能的起始点
        for (int i = 0; i < wordLen; i++) {
            unordered_map<string, int> currentCount;
            int left = i, count = 0;
            for (int j = i; j <= s.length() - wordLen; j += wordLen) {
                string word = s.substr(j, wordLen);
                
                if (wordCount.count(word)) {
                    currentCount[word]++;
                    count++;
                    
                    while (currentCount[word] > wordCount[word]) {
                        string leftWord = s.substr(left, wordLen);
                        currentCount[leftWord]--;
                        count--;
                        left += wordLen;
                    }
                    
                    if (count == words.size()) {
                        result.push_back(left);
                        string leftWord = s.substr(left, wordLen);
                        currentCount[leftWord]--;
                        count--;
                        left += wordLen;
                    }
                } else {
                    currentCount.clear();
                    count = 0;
                    left = j + wordLen;
                }
            }
        }
        
        return result;
    }
};

优化解释

  1. 多起始点滑动窗口

    • 我们遍历每一个可能的起始点 i,从 0 到 wordLen-1。这样可以保证所有可能的起始位置都被考虑到。
  2. 滑动窗口调整

    • 在滑动窗口内,我们通过右移窗口并增加 currentCount 来记录单词的出现次数。
    • 如果当前单词出现次数超过 wordCount 中记录的次数,我们通过左移窗口来调整窗口大小,直到窗口内的单词出现次数符合要求。
    • 当窗口内单词的总数等于 words.size() 时,表示我们找到一个有效的起始位置。
  3. 提前退出

    • 如果遇到一个不在 words 中的单词,我们立即清空 currentCount 并重置窗口,避免不必要的计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翔山代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值