刷题39-最小覆盖子串(滑动窗口)

原题链接

题目描述

给出两个字符串 S 和 T,要求在O(n)的时间复杂度内,在 S 中找出最短的,包含 T 中所有字符的子串。
例如:
S ="XDOYEZODEYXNZ"
T ="XYZ"
找出的最短子串为"YXNZ"
注意:
如果 S 中没有包含 T 中所有字符的子串,返回空字符串 “”;
满足条件的子串可能有很多,但是题目保证满足条件的最短的子串唯一。

示例

输入:
"XDOYEZODEYXNZ","XYZ"
输出:
"YXNZ"

参考解析

这道题的思路是:
1) begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
记录窗口长度d
2) 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
包含在窗口,
3) 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
4) 如此循环知道end到S中的最后一个字符。
时间复杂度为O(n)

参考代码

/**
 * 最小覆盖子串
 * 
 * @param S S覆盖T中所有元素的最小子串
 * @param T
 * @return
 */
public String minWindow(String S, String T) {
    
    
	// 统计T中每个字符出现的次数.
	// 这个操作经常用到,128覆盖了常见字符的ASCII码的值
	int map[] = new int[128];
	for (int i = 0; i < T.length(); i++) {
    
    
		map[T.charAt(i)]++;
	}
	// 首尾指针
	int begin = 0, end = 0;
	// 窗口的大小
	int d = Integer.MAX_VALUE;
	// counter记录T中还有几个字符没被窗口包含
	int counter = T.length();
	// 窗口左侧的值的下标
	int head = 0;
	// map[] > 0 说明该字符在T中出现,
	// counter-- 表示对应的字符被包含在了窗口,
	// map[S.charAt(end)]--, 如果s中的字符没有在T中出现,则map[]中对应的字符-1后变为负值
	while (end < S.length()) {
    
    
		if (map[S.charAt(end)] > 0) {
    
    
			counter--;
		}
		map[S.charAt(end)]--;
		end++;

		// 当counter==0时,说明窗口已经包含了T中的所有字符
		while (counter == 0) {
    
    
			// end前面已经有一个end++的操作,这里就不需要end-begin+1了。
			if (end - begin < d) {
    
    
				head = begin;
				d = end - head;
			}
			// begin开始后移,继续向后寻找。
			// 如果begin后移后指向的字符在map中==0,表示是在T中出现的,
			// 如果没有出现,map[]中的值会是负值。
			// 在T中的某个字符从窗口中移除,所以counter++。
			if (map[S.charAt(begin)] == 0) {
    
    
				counter++;
			}
			// 之前减过一次,现在加回来
			map[S.charAt(begin)]++;
			begin++;

		}
	}
	return d == Integer.MAX_VALUE ? "" : S.substring(head, head + d);
}

猜你喜欢

转载自blog.csdn.net/Awt_FuDongLai/article/details/111032232
今日推荐