leetcode 含有所有字符的最短字符串

题目链接
思路:滑动窗口
分析:s要包含t中所有字母,可以拆分为两个问题
1.s的子字符串中是否包含t中所有字母
2.如果包含,如何将这个子字符串缩短,并且要满足前提包含t中所有字母。

解决问题1:
要判断是否包含,并且这个判断的时间复杂度要常数级,那么我们可以将t中所有字母放到一个int数组中,因为包含大小写字母,一共26+26,最大长度为52,声明ts[52]。然后每次判断只需要去比对这个数组即可。

解决问题2:
定义一个左指针,指向0,右指针指向0,每次移动一下右指针,都判断一下是否包含了t中所有字母,如果在当前右边指针和左边指针包含了t中所有字母,那么开始移动左边指针,每次移动左边指针,也判断是否包含了t中所有字母,如果包含,继续左移,直到不包含,那么更新长度等于 右边指针位置 - 左边指针位置 + 1 + 1。
代码:

class Solution {
    public String minWindow(String s, String t) {
    	//t的长度大于s的长度肯定不包含
        if(t.length()>s.length()){
            return "";
        }
		//因为有大小写字母  26 + 26 所以用长度为52的数组来记录
		
        //words 用来记录滑动窗口中的字符的情况
        int[] words = new int[52];
        
        //ts用来记录t的字符情况
        int[] ts = new int[52];
        //i表示右边的指针
        int i = 0;
        //left表示左边的指针
        int left = 0;

		//遍历建立ts  并且顺带建立滑动窗口中字符的情况words
        for(; i < t.length() ; i++){
        	//t的情况
            if(t.charAt(i)>='A' && t.charAt(i)<='Z'){
                ts[t.charAt(i)-'A']++;
            }else{
                ts[t.charAt(i)-'a'+26]++;
            }
            //滑动窗口的情况
            if(s.charAt(i)>='A' && s.charAt(i)<='Z'){
                words[s.charAt(i)-'A']++;
            }else{
                words[s.charAt(i)-'a'+26]++;
            }
        }
        //长度
        int res = Integer.MAX_VALUE;
        //答案的左边
        int minLeft = -1;
        //答案的右边
        int minRight = -1;

		//遍历s
        for(;i<s.length();i++){
        	//judgeII是判断words是否包含了t中所有的字符串
            if(judgeII(words,ts)){
            	//如果包含了
                while (true){
                    int index = 0;
                    //将左边第一个移除
                    if(s.charAt(left)>='A' && s.charAt(left)<='Z'){
                        index = s.charAt(left) - 'A';
                        words[index]--;
                    } else {
                        index = s.charAt(left) - 'a' + 26;
                        words[index]--;
                    }
                    left++;
                    
					//如果满足下列条件,说明出去了一个需要的字符,那么此时肯定已经不满足了words包含ts,否则就继续将左边的指针右移
                    if(words[index] < ts[index]){
                        break;
                    }
                }
                //判断是不是比现在的答案还短,更短的话,就更新
                if(res > i-left+2){
                    res = i - left + 2;
                    minLeft = left-1;
                    minRight = i-1;
                }
            }
			
			//将右边的字符加入words,也就是滑动窗口
            if(s.charAt(i)>='A' && s.charAt(i)<='Z'){
                words[s.charAt(i)-'A']++;
            }else{
                words[s.charAt(i)-'a'+26]++;
            }

        }
        //下面就是判断最后一次,就是右边到达最后,此时特殊处理一下,再判断一下即可
        if(judgeII(words,ts)){
            while (judgeII(words,ts)){
                if(s.charAt(left)>='A' && s.charAt(left)<='Z'){
                    words[s.charAt(left)-'A']--;
                }else{
                    words[s.charAt(left)-'a'+26]--;
                }
                left++;
            }
            if(res > i-left+2){
                res = i - left + 2;
                minLeft = left-1;
                minRight = i-1;
            }
        }

        if(minLeft==-1){
            return "";
        }
        return s.substring(minLeft,minRight+1);
    }
	
	//这个方法就是 时间复杂度O(1)的判断滑动窗口中是否包含t的方法
    public boolean judgeII(int[] words,int[] ts){
        for(int i = 0;i<ts.length;i++){
            if(ts[i]>words[i]){
                return false;
            }
        }
        return true;
    }
}

这个题目还可以继续优化,这里右边指针移动,我们就需要调用judgeII方法,这个方法判断需要遍历长度为52的数组,其实可以只要1次就可以解决的。
优化思路:记录t的长度,每次当滑动窗口中进入一个字符,并且该字符在加入滑动窗口后,对应的数组中的位置的值小于等于ts中该字符对应的位置的值,那么就说明进入了一个有效字符,也就是t中包含的字符,那么n–,当n=0的时候,说明words中就全部包含了t中的字符,这样就不用每次移动右边的指针就去遍历长度为52的数组了,而只需要比对一次就行。后面的思路修改也是如此,不过总的来说,两种思路都是O(n),不过一个是O(52*n),一个是真的O(n)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值