76. Minimum Window Substring【力扣】

题意理解

给定一个字符串T,问在另一个字符串S中包含这个字符串的字符的最小长度子字符串。字符顺序可以颠倒

问题分析

滑动窗口 + hashtable做 (双指针法)

其他

比较字符串是否包含,可以用hashmap或字典(每个字符占一个元素位置),字典的速度比map要快

我的方法耗时太长,这里有O(n)的算法。慢慢看。

https://leetcode.com/problems/minimum-window-substring/discuss/26840/Sharing-my-straightforward-O(n)-solution-with-explanation

看完了。几个优化点

一次循环中,同时处理扩展右下标和缩小左下标。

两个map的比较,我是直接找到了全部的t字符再比较,这个方法是逐步删除了左边不满足的字符来比较。搜索的范围小了很多。

不用把所有合适的都保存下来,再从中找最小的那个,而是每次找到一个新字符串,就更新最小的字符串。

每次都是把字符串取下来,再传入函数和t的map比较,这个方法是设置了子串的长度,如果长度满足就狂缩子串,再更新最小字符串。

for循环用了一个技巧,两个if分别对应不同的实际,一个是扩右侧字符,一个是缩左侧字符,互不干扰。

双指针方法,命令很好,low表示慢指针,指向子串开始,high表示快指针,指向子串结尾。

链接

class Solution {
public:
    string minWindow(string s, string t) {
        int len = s.size();
        int left = 0, right = 0;
        string result;
        int minSize = INT_MAX;
        
        map<char,int> map_t;
        map<char,int> map_s;
        
        for(char a : t)    //构造字符串map
            map_t[a]++;
        
        map_s[s[0]]++;
        
        int preOK = 0;
        while(right < len)
        {
            //cout << left << '\t' << right << endl;
            string curWin =s.substr(left, right - left + 1);    //获取当前字符串
            //cout << curWin << endl;
            
            if (isContain(map_s, map_t))    //当前字符map是否包含t_map
            {
                //cout << curWin << " is contained" << endl;
                if (left + 1 > right)    //如果加一位当前字符串存在
                {
                    
                    if (curWin.size() < minSize)    //比最小的字符串还短
                    {
                        result = curWin;    //更新最短字符串
                        minSize = curWin.size();    //更新最短字符串大小
                        //cout << "result " << result << endl;
                    }
                    right++;    //扩大字符串
                    map_s[ s[right] ]++;    //修改map_s的映射
                    //cout << "add right " << s[right] << endl;
                }
                else    //当前字符串不存在了
                {
                    preOK = 1;    //当前字符串是正确的
                    map_s[ s[left] ]--;    //更新map_s的映射
                    //cout << "remove left " << s[left] << endl;
                    left++;    //缩小字符串
                }
            }
            else    //当前字符串不包含t
            {
               // cout << curWin << " is not contained" << endl;
                if (preOK == 1)    //之前设置了OK
                {
                    //minStrs.push_back(s.substr(left - 1, right - left + 2));
                    string curWin = s.substr(left - 1, right - left + 2); //获取之前的子字符串
                    if (curWin.size() < minSize)    //更新到最小字符串中
                    {
                        result = curWin;
                        minSize = curWin.size();
                        //cout << "result " << result << endl;
                    }
                    preOK = 0;    //重置OK
                }
                right++;    //扩大字符串,向后找
                map_s[ s[right] ]++;    //增加map_s的映射
            }
        }

        return result;
    }
    bool isContain(map<char, int> map_s, map<char, int> map_t)
    {
        if (map_s.size() < map_t.size())    //s长度短,肯定不能包含t
            return false;
        bool flag = true;
        for(auto it = map_t.begin(); it != map_t.end(); it ++)    //遍历t
        {
            char tChar = it -> first;    //获取字符
            int tInt = it -> second;    //获取数量

            if(map_s[tChar] < tInt)    //有一个s的字符数量小于t
            {
                flag = false;    //失败
                break;
            }
        }
        return flag;
    }
};

 

点赞