leetcode 76. Minimum Window Substring解析

题目是下面
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

Example:

Input: S = “ADOBECODEBANC”, T = “ABC”
Output: “BANC”

给出字符串S和pattern T,找出包含T中所有字符的最短子字符串。
个人觉得这道题难点主要在各种细节。

思路进化:

待改进思路:
刚开始也是想到2 pointers,left和right。left先不动,right向右走,当left和right中间的字符串包含T中所有字符时,left和right都移向right的下一字符。
至于怎么判断是否包含T中所有字符,先用一个hashSet保存T中所有的char,然后再用一个tmp的hashSet保存,当size一样的时候就得到一个substring,这时再把tmp给clear掉。

改进点1:
这个方法有个问题,hashSet是不能保存重复char的,所以出现重复char的时候,如下例:
S = “a”, T = “aa”
这时塞进hashSet的char是一样的,会判断为是一样

所以需要用一个hashMap,key是T的当前char,Value是这个char出现的次数

改进点2:
left和right不能同时移到right的下一位,例如下面
S = “bbab”, T = “ab”
left = 0, right = 2时满足一个substring,这时left和right同时移到right的下一位,所以left和right都移到最右边的b,所以,最右边的最小字符串“ab”就被跳过去了,所以此方法不可行

解题思路:

根据上述改进点1,因为T中会有重复的字符,所以要记录下它的count,所以这里用一个hashMap,当然还有一种方法是定义一个char类型的数组,size128,包含所有char,然后数组内保存count,效果和hashMap一样,只不过O(1) 访问count。这里的方法用hashMap。

定义一个hashMap<Character, Integer> wordCount保存char和它出现的次数count

	for (int i = 0; i < t.length(); i++) {
            count = wordCount.getOrDefault(t.charAt(i), 0) + 1;
            wordCount.put(t.charAt(i), count);
    }

定义两个pointer,left和right。作一个sliding window。刚开始left和right都指向0,然后right右移。

问题1: 如何判断全部匹配了T中的字符?
当s.charAt(right)的char包含在T中时,取出char对应的count,并把count-1, 当count减到0时,算是匹配了一个字符,可以用一个matchCount变量记录,注意这个match对重复出现的字符也是unique的,当matchCount== wordCount.size()的时候就说明完全匹配了。

			ch = s.charAt(right);
            count = wordCount.get(ch);
            if (count == null) {
                right++;
                continue;
            }
            wordCount.put(ch, count - 1);
            //1 -> 0..
            if (count == 1) {
                matchCount ++;
            }

例如:
S = “ADOBBCODEBANC”, T = “ABC”
这时left指向0,right指向5,substring是“ADOBBC”,长度是6,
这时用minLen变量记录下长度6,起始点index记录下这时的left

这时hashMap的内容是
<A, 0> <B, -1> <C, 0>
因为A出现一次,所以count-1=0
B出现2次,count-2=-1
C出现1次,count-1=0
B出现一次到达0后,出现第二次是仍然需要-1的,因为left右移时要用到。

这时substring已经包含了T中的所有字符,需要做的是缩减substring的长度,以便找到长度更短的substring,right向右移可以保证substring仍然有效,但是长度会变长,所以把left向右边移,以缩短长度。

当left指向的字符在T之内的时候,需要把s.charAt(left)对应的count++(因为前面说了count==0时表示字符匹配,移出去的时候不匹配了count就要+1),同时left++(右移)。
注意count由0变为1时,表示匹配的字符已经不在left和right构成的sliding window里了,matchCount需要-1
left指向的字符不在T之内的时候,直接将left++,所以这里不用if而用while来处理,处理什么,因为substring的length在减小,所以需要更新minLen和index

            while (matchCount == wordCount.size()) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    index = left;
                }
                ch = s.charAt(left);
                count = wordCount.get(ch);
                if (count == null) {
                    left++;
                    continue;
                }
                //0 -> 1..
                if (count == 0) {
                    matchCount--; 
                }
                wordCount.put(ch, count + 1);
                left++;
            }

最后,当minLen没有得到更新,一直是最大值的时候,就说明S中没有包含T的字符串,返回“”

完整代码:

    public String minWindow(String s, String t) {
        if (s == null || t == null) {
			return "";
		}
		if (s.length() == 0 || t.length() == 0 || s.length() < t.length()) {
            return "";
        }
        
        HashMap<Character, Integer> wordCount = new HashMap<>();
        Integer count = 0;
        int minLen = Integer.MAX_VALUE;
        int left = 0;
        int right = 0;
        char ch = 0;
        int matchCount = 0;
        int index = 0;
        
        for (int i = 0; i < t.length(); i++) {
            count = wordCount.getOrDefault(t.charAt(i), 0) + 1;
            wordCount.put(t.charAt(i), count);
        }
        
        while (right < s.length()) {
            ch = s.charAt(right);
            count = wordCount.get(ch);
            if (count == null) {
                right++;
                continue;
            }
            wordCount.put(ch, count - 1);
            //1 -> 0..
            if (count == 1) {
                matchCount ++;
            }
            
            while (matchCount == wordCount.size()) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    index = left;
                }
                ch = s.charAt(left);
                count = wordCount.get(ch);
                if (count == null) {
                    left++;
                    continue;
                }
                //0 -> 1..
                if (count == 0) {
                    matchCount--; 
                }
                wordCount.put(ch, count + 1);
                left++;
            }
            right++;
        }
        return minLen==Integer.MAX_VALUE ? "" : s.substring(index, index + minLen);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蓝羽飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值