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).

For example,
S = "ADOBECODEBANC"
T = "ABC"

Minimum window is "BANC".

Note:
If there is no such window in S that covers all characters in T, return the empty string "".

If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

 

Subscribe to see which companies asked this question

 

算法思路:

网上看了一些博客的思路,综合自己的想法,总结以下:

 

首先分析条件:

1.T中字符可能有重复;

2.S包含T的条件:S中包含所有T中的字符;T中所有字符出现次数都不大于S中对应字符出现次数;

根据以上,算法思路为:首先对T中出现的字符以及出现次数做统计,之后查找S中是否有对应字符,以及对应字符出现次数是否不小于T中出现次数。

 

其次设计数据结构:

为了统计字符串中出现的字符以及次数,使用其ASCII码作为Hash表索引,利用一个长度为128的数组(英文字母ASCII码值不超过128)来存储出现字符,以及字符出现次数。例如如果字符'A'出现了3次,则统计数组c['A']=3(相当于c[65]=3,因为'A'的ASCII码是65)。

 

之后设计算法细节:

1.设置2个指针start和end,分别指向S中包含T的最小Window的开始和结束位置。

2.由于要找到包含T的最小Window,所以需要遍历整个字符串。又由于时间复杂度为O(n),所以只能遍历一次。

3.为了比较S中字符出现次数与T中字符出现次数,用一个标志count来计数。count表示T的长度,当S中找到的字符次数都与T中字符次数对应相等,并且count等于T的长度,则可知找到了包含T的Window。

4.为了判断Window是否最小,用一个minwin来存储找到的Window。

 

算法大致过程如下:

1.遍历T,将T中字符出现次数存入数组ta[];新建数组sa[]存储S中字符出现次数;

2.设置指针start和end,都指向S开始位置;

3.end向后遍历,若遇到的字符在ta[]中的值不为0(即T中存在的字符),则有两种情况:一、sa[]中该字符值等于ta[]中该字符值,则说明该字符已经找够,不需要再找,则忽略该字符,继续遍历;二、sa[]中该字符值小于ta[]中该字符值(sa[]中该字符值可能为0,即未出现过的字符),则说明该字符还未找全,将sa[]中该字符值加1,count加1,继续遍历。若遇到的字符在ta[]中的值为0,忽略该字符,继续遍历;

4.当count值等于T的长度,则说明已经找到了T中所有元素,end暂停遍历;此时关注start,在此时的start处或者start向后一段内,可能都是T中不存在的字符,但由于这些字符未计入count值,所以是否包含这些字符不影响判断条件;但是为了找到最小的Window,需要start向后遍历,将T中不存在的字符都排除,找到T中存在的第一个字符位置,即找到了一个包含T的Window;将此时的Window长度与minwin长度比较,若小于minwin长度,说明找到了更小的Window,更新minwin为当前找到的Window;

5.找到一个Window后end继续遍历。每当end后移一步,就考察start处字符,若start处字符次数大于T中该字符次数,则start后移,若start处字符不存在T中,start也后移,直到找到包含T的最小Window,再进行记录。

6.直到end遍历到S的结尾位置,算法结束。


算法运行流程:

S=“ADOBECODEBANC”

T=“ABC”

其中不相同的字符有7个:ABCDEON,故ta = {1,1,1,0,0,0,0}

现在开始遍历S串中的字符

1)start、end与count初始都默认为0,且start与end都是S字符串中的下标。现从end开始向后遍历

2)当end=5时(即end指向S中第一个C时)  

    sa={1,1,1,1,1,1,0}

    start=0,

    count=t.length; 

    此时的字符串为“ADOBEC” ,但是此时start不满足向后遍历的条件所以当前S中最短的字符串就是“ADOBEC”

3)经过步骤2后,end继续向后遍历,一直到end=10时(即遍历到第二个A时,此时start也指向的是A)

    sa={2,2,1,2,2,2,0}

    start=0

    count=t.length

    此时start满足向后遍历的条件(即找到S中新的子字符串包含T中所有元素。注:此时还要将sa中的A的出现次数-1),故start一直会遍历到start=5位置(start指向第一个C时),此时的目标字符串是“CODEBA”,其长度与上一个目标字串“ADOBEC”相等。

4)此时end还没有结束它的遍历,与步骤3)同理当end遍历到S中最后一个字符C时,start可以略过第一个C(因为C只在T中出现一次,故只能在S中出现一次)以及随后的ODE(其均为在T只能够出现)知道B(当前B在S中只出现了一次)为止,得到“BANC”为最短的目标子字符串



代码:

	public String minWindow(String s, String t) {
		int[] ta = new int[128]; // 将T中字符出现次数存入数组ta[]
		int[] sa = new int[128]; // 存储S中字符出现次数;
		int min = Integer.MAX_VALUE;
		String minwin = "";
		for (int i = 0; i < t.length(); i++)
			ta[t.toCharArray()[i]]++; // 统计次数

		int count = 0;
		int end = 0;
		int start = 0;

		// 先从end向后移动
		while (end < s.length()) {
			if (ta[s.charAt(end)] != 0) { // 若遇到的字符在ta[]中的值不为0(即T中存在的字符时
				// 一、sa[]中该字符值等于ta[]中该字符值,则说明该字符已经找够,不需要再找,则忽略该字符,继续遍历;
				/*
				 * 二、sa[]中该字符值小于ta[]中该字符值(sa[]中该字符值可能为0,即未出现过的字符),
				 * 则说明该字符还未找全,将sa[]中该字符值加1,count加1,继续遍历。
				 */
				//只有当s中的字符比t中字符出现次数少时 count才++
				if (sa[s.charAt(end)] < ta[s.charAt(end)]) {
					count++;
				}
				sa[s.charAt(end)]++;	//为sa数组赋值
			}

			// 当选中的{start...end}已经包含所有T中字符时
			if (count == t.length()) {
				// 次数start往后移动,先是排除无关字符,而后是比较最小window
				// 1.若sa[s.charAt(start)]不是T里的字符时,start下标后移
				// 2.若当前sa数组中start下标所在的位置是多余的,则也可以将start其后移
				while (ta[s.charAt(start)] == 0
						|| sa[s.charAt(start)] > ta[s.charAt(start)]) {
					if (sa[s.charAt(start)] > ta[s.charAt(start)]) {
						sa[s.charAt(start)]--;
					}
					start++;
				}
			}
			
			if (end - start + 1 < min) {
				minwin = s.substring(start, end + 1);
				min = end - start + 1;
			}
			//每当end后移一步,就考察start处字符,若start处字符次数大于T中该字符次数,则start后移
			//若start处字符不存在T中,start也后移
			end++;
		}

		return minwin;
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值