解题法-滑动窗口

 3. 无重复字符的最长子串

使用set来存储窗口

    int lengthOfLongestSubstring(string s) {
        unordered_set<char> set;
        int ans = 0, right = 0;
        
        for (int i=0; i < s.length(); i++)
        {
            // 右指针右移,直到遇到重复字符
            while (right < s.length() && 0==set.count(s[right]))
            {
                set.insert(s[right]);
                right++;
            }
            // 计算下当前的长度
            ans = max(ans, right - i);
            // 移除最左边的重复字符
            if (set.size() > 0)
            {
                set.erase(s[i]);
            }
          
        }

        return ans;

 30. 串联所有单词的子串

继续滑动窗口,使用了两个hash,tFreq是记录目标窗口中各个单词出现的频数,winFreq记录当前窗口中各个单词出现的频数,同时使用distance记录两个窗口的差异。

由于是单词比较,s 的起点不同,所划分的单词也不同,所以大循环是用单词长度划分 s

在窗口的滑动中,要处理下面3中情况

  1. 新的单词并不在words里
  2. 遇到了重复次数过多的单词
  3. 完全匹配
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans = {};
        unordered_map<string, int> winFreq, tFreq; // 当前窗口的单词出现频数 和 目标的单词出现频数
        int left=0, right = 0, distance = words.size(); // distance 表示 当前窗口与目标窗口的差异, 0 表示完全匹配
        int lenWords = 0;
        int lenS = s.length();

        if (words.size() == 0 || s.length() == 0)
        {
            return ans;
        }
        int wordSize = words[0].size();
        lenWords = wordSize * words.size();
        if (lenS < lenWords)
        {
            return ans;
        }
        
        for (string s : words)
        {
            tFreq[s]++;
        }
        
        // 将 s 以单个单词长度分类
        for (int start = 0; start < wordSize; start++)
        {
            // 每一轮的初始条件(曾因为 distance 和 winFreq 没有重置导致多次提交失败)
            left = start, right = start, distance = words.size(), winFreq.clear();

            while(right + wordSize <= lenS)
            {
                string subStr = s.substr(right, wordSize);
                // 1. 存在, 是目标单词
                if (tFreq.find(subStr) != tFreq.end())
                {
                    winFreq[subStr]++;
                    distance--;
                    //  有重复,需要去重, 缩小窗口
                    while (winFreq[subStr] > tFreq[subStr]) 
                    {
                        string firstStr = s.substr(left, wordSize);
                        winFreq[firstStr]--;
                        distance++;
                        left += wordSize;
                    }  
                }
                 // 2. 不存在
                else
                {
                    left = right + wordSize;
                    distance = words.size();
                    winFreq.clear();
                }

                if (0 == distance)
                {
                    ans.push_back(left);
                }  

                right += wordSize;
            }
            
        }
        return ans;
    }
};

76. 最小覆盖子串

继续滑动,保持左边窗口不变,右窗口滑动,直到匹配所有字符,然后考虑缩减左边窗口;

数据结构上,使用hash记录当前窗口是否已经包含了t中的所有字符;同时利用负数表示不需要的字符,正数表示需要的字符,避免了使用find

class Solution {
public:

    string minWindow(string s, string t) {
        // set hash queue stack list tree
        const int lenS = s.length(), lenT = t.length();
        int ansLen = INT_MAX, start = 0;
        int left = 0, right = 0, k = lenT;// k 记录当前所需字符数

        vector<int> need(128, 0); // 记录当前窗口下t中各个字符缺少的个数
        // 初始化,缺t中的所有字符
        for (int i=0; i<lenT; i++)
        {
            need[t[i]]++; 
        }

        for (right = 0; right < lenS; right++)
        {
            if (need[s[right]] > 0)
            {
                k--;
            }
            need[s[right]]--; // 需要数减 1
            if (k == 0) // found all 
            {
                // 尝试缩减窗口
                while (left < right && need[s[left]] < 0) // 窗口左边存在不需要的字符
                {
                    need[s[left]]++;
                    left++;
                }
                // 遇到了第一个需要的字符,此时窗口符合要求
                if (right - left + 1 < ansLen)
                {
                    ansLen = right-left+1;
                    start = left;
                }
                // 窗口左移一位,释放need[s[left]]
                need[s[left]]++;
                left++;
                k++;
            }
        }
        
        if (ansLen == INT_MAX)
        {
            return "";
        }
        else
        {
            return s.substr(start, ansLen);
        }

    }
};

159. 至多包含两个不同字符的最长子串

使用hash记录当前窗口下各个字符的个数,使用计数器count记录窗口中不相同的字符个数;当count > 2时,就需要记录当前「可行」窗口大小,并缩小左边窗口,删除1个旧字符。

考虑的特殊情况有:

  1. s 中只有2个不相同的字符
  2. s 的长度小于3
    int lengthOfLongestSubstringTwoDistinct(string s) {
        int lenS = s.length();
        int left =0, right = 0;
        int ans = 0;
        int count = 0, k = 2;
        unordered_map<char, int> hash;

        if (lenS <= k)
            return lenS;

        for (right =0; right < lenS; right++)
        {
            hash[s[right]]++;
            if (hash[s[right]] == 1) // 遇到新字符
            {
                count++;
            }
            // 遇到了第三个字符,缩减左边窗口,删除1个字符
            if (count > k)
            {
                // 计算当前子串长度
                ans = max(ans, right-left);
                while(count > k && left < right)
                {
                    hash[s[left]]--;
                    if (hash[s[left]] == 0)
                    {
                        count--;
                    }
                    left++;
                }
            }
        }
        ans = max(ans, right-left); // 考虑只有2个非重复字符的情况
        return ans;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值