LeetCode - 解题笔记 - 76 - Minimum Window Substring

Solution 1

这道题根据名称和内容描述,基本可以判断是使用滑动窗口,也就是同向双指针进行最有区间的寻找(而非维护)。整体思路就是先移动右指针以扩展区间寻找能够满足要求的区间,然后再移动左指针以压缩空间,从而找到满足要求的最小区间。其中的「满足要求」的实现思路根据要求获得一个计数器,根据字符个数记录满足状态。这里我选择额外使用一个状态量保存当前满足要求的字符个数,省着每次都有遍历一遍计数器。

整体思路:

  1. 整理目标字符串,更新T计数器
  2. 在原始字符串上移动右指针,更新S计数器,并更新满足要求状态量
  3. 如果满足要求状态量不够,继续移动右指针
  4. 否则
    1. 移动左指针,更新S计数器,并更新满足要求状态量
    2. 如果状态量不够,跳出
    3. 否则,根据压缩的情况更新最优区间
  • 时间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n分别为两个字符串的长度,因为需要进行一次线性遍历
  • 空间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n分别为两个字符串的长度,需要创建两个计数器,最坏情况两个字符串所有字符都不相同
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map <char, int> counterS, counterT;
        
        // 整理t中的字符情况
        for (const auto &c: t) {
            counterT[c]++;
        }
        // 所有需要满足的字符个数
        int uniqueT = counterT.size();
        
        int ansLen = -1, ansLeft = 0, ansRight = 0;
        int leftP = 0, rightP = 0;
        int uniqueS = 0; // 这里指满足T的字符个数

        while (rightP < s.size()) {
            // cout << "E " << leftP << " " << rightP << " " << s.substr(leftP, rightP - leftP + 1) << " " << ansLen << " " << ansLeft << " " << ansRight << endl;
            // 剪枝:只考虑T中存在的字符
            if (counterT.find(s[rightP]) != counterT.end()) {
                counterS[s[rightP]]++;
                if (counterS[s[rightP]] == counterT[s[rightP]]) {
                    // 满足要求
                    uniqueS++;
                }
            }
            
            // 在满足要求时,左指针压缩(while里面直接作为触发判断了)
            while (uniqueS == uniqueT && leftP <= rightP) {
                // cout << "C " << leftP << " " << rightP << " " << s.substr(leftP, rightP - leftP + 1) << " " << ansLen << " " << ansLeft << " " << ansRight << endl;
                if (ansLen == -1 || rightP - leftP + 1 < ansLen) {
                    ansLen = rightP - leftP + 1;
                    ansLeft = leftP;
                    ansRight = rightP;
                }
                // 左指针不断右移,直到不满足要求
                if (counterT.find(s[leftP]) != counterT.end()) {
                    counterS[s[leftP]]--;
                    if (counterS[s[leftP]] < counterT[s[leftP]]) {
                        // 不再满足要求
                        uniqueS--;
                    }
                }
                leftP++;
            }
            rightP++;
        }

        return ansLen == -1? "": s.substr(ansLeft, ansRight - ansLeft + 1);
    }
};

Solution 2

Solution 1的Python实现

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        counterS, counterT = dict(), dict()
        
        for c in t:
            counterT[c] = counterT[c] + 1 if c in counterT else 1
        
        uniqueT = len(counterT)

        ansLen, ansLeft, ansRight = -1, 0, 0
        leftP, rightP = 0, 0
        uniqueS = 0
        
        while rightP < len(s):
            # print("E", leftP, rightP, s[leftP: rightP + 1], uniqueS, uniqueT, ansLen, ansLeft, ansRight)
            if s[rightP] in counterT:
                counterS[s[rightP]] = counterS[s[rightP]] + 1 if s[rightP] in counterS else 1
                if counterS[s[rightP]] == counterT[s[rightP]]:
                    uniqueS += 1
            
            while uniqueS == uniqueT and leftP <= rightP:
                # print("C", leftP, rightP, s[leftP: rightP + 1], uniqueS, uniqueT, ansLen, ansLeft, ansRight)
                if ansLen == -1 or rightP - leftP + 1 < ansLen:
                    ansLen = rightP - leftP + 1
                    ansLeft = leftP
                    ansRight = rightP
                
                if s[leftP] in counterT:
                    counterS[s[leftP]] -= 1
                    if counterS[s[leftP]] < counterT[s[leftP]]:
                        uniqueS -= 1
                
                leftP += 1
                
            rightP += 1
                
        return "" if ansLen == -1 else s[ansLeft: ansRight + 1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值