leetcode-76-最小覆盖子串-java

本文深入探讨了如何寻找一个字符串中包含特定字符集的最短连续子串,通过两个不同实现方式,详细讲解了滑动窗口算法在解决此类问题中的应用。提供了完整的Java代码示例,帮助读者理解算法原理及其在实际编程中的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目及测试

package pid076;
/*最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:

    如果 S 中不存这样的子串,则返回空字符串 ""。
    如果 S 中存在这样的子串,我们保证它是唯一的答案。


*/


public class main {
	
	public static void main(String[] args) {
		String [] testTable = {"ADOBECODEBANC","BANC"};
		String [] testTable2 = {"ABC","BC"};
		for (int i=0;i<3;i++) {
			test(testTable[i],testTable2[i]);
		}
	}
		 
	private static void test(String ito,String ito2) {
		Solution solution = new Solution();
		String rtn;
		long begin = System.currentTimeMillis();
		System.out.println(ito);
		System.out.println(ito2);
		//开始时打印数组
		
		rtn= solution.minWindow(ito,ito2);//执行程序
		long end = System.currentTimeMillis();	
		
		System.out.println("rtn" );
		System.out.print(rtn);
		System.out.println();
		System.out.println("耗时:" + (end - begin) + "ms");
		System.out.println("-------------------");
	}

}

解法1(别人的)

明显是要使用滑动窗口,比较复杂的一点是,滑动窗口的长度是可变的。
所以区别于固定滑动窗口的达到窗口长度后随着右指针向右移动一位左指针也要移动一位
滑动窗口更改长度的原因是在滑动窗口中去掉不需要的字符

使用数组hash存储需要的字符串,根据字符串t,里面hash[ tt[i] -'0']++,需要的字符为正数,不需要的为0或者负数。

使用count记录匹配的字符数,一开始为t.length,如果为0,代表完全匹配,刷新minLength和results

right每次向右移动一位,hash[ ss[right] -'0']- - ,表示需要的字符数减少,如果hash[ ss[right] -'0']>=0 代表right是需要的字符。

然后不断让left向右移hash[ ss[left] -'0']<0 ,代表left是不需要的字符,自然可以向右移。

最后判断count是否为0

class Solution {
    public String minWindow(String s, String t) {
        
        char[] ss = s.toCharArray();
        char[] tt = t.toCharArray();
        //用来存储滑动窗口中的值(注意还有小写的)
        int[]hash = new int[256];
        //最小子串的长度
        int minlength = s.length();
        //最小子串
        String results = "";
        for(char smallt:tt)
        {
            hash[smallt-'0']++;
        }
        int left = 0;
        int right = 0;
        int count = tt.length;
        for(;right<ss.length;right++)
         {
             hash[ss[right]-'0']--;
             //说明当前的字符存在于T中,且当前滑动窗口中还需要该字符
             //后面这个且的意思就是比如我的T为ABC,我只有第一次遇到A才会count--,而第二次就不会了,
             if(hash[ss[right]-'0']>=0)
             {
                count--;
             }
             //right又遇到了left处的字符(特指遇到T中存在的),或left处的字符不是T中需要的,就右移左指针直到找到需要的或者left=right
             while(left<right&&hash[ss[left]-'0']<0)
             {
                hash[ss[left]-'0']++;
                left++; 
             }
            //这里大于等于是防止最小覆盖子串就是s其本身
            if(count==0&&minlength>=right-left+1)
            {
                minlength = right-left+1;
                results = s.substring(left,right+1);
            }
         }
         return results;
    
    }
}

解法2(成功,5ms,较快)

class Solution {
	public String minWindow(String s, String t) {
		if(t.length() == 0 || s.length() == 0) {
			return "";
		}
		int[] base = new int[58];
		for(int i=0;i<t.length();i++) {
			base[t.charAt(i) - 'A']++;
		}
		int[] contain = new int[58];
		int valid = 0;
		int left = 0;
		int right = 0;
		int min = Integer.MAX_VALUE;
		String result = "";
		while(left <s.length()) {
			if(valid < t.length()  && right <s.length()) {
				int nowIndex = s.charAt(right) - 'A';
				contain[nowIndex]++;
				right++;
				if(contain[nowIndex] <= base[nowIndex]) {
					valid++;
				}
			}else {
				int nowIndex = s.charAt(left) - 'A';
				contain[nowIndex]--;
				left++;
				if(contain[nowIndex] < base[nowIndex]) {
					valid--;
				}
			}
			if(valid == t.length()) {
				if(right - left < min) {
					min = right - left;
					result = s.substring(left, right);
				}
			}
		}
		return result;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值