最小覆盖子串

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

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”

说明:

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

思路和算法

本问题要求我们返回字符串 s 中包含字符串 ttt 的全部字符的最小窗口。我们称包含 t 的全部字母的窗口为「可行」窗口。

我们可以用滑动窗口的思想解决这个问题,在滑动窗口类型的问题中都会有两个指针。一个用于「延伸」现有窗口的 right 指针,和一个用于「收缩」窗口的 left 指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s 上滑动窗口,通过移动 right 指针不断扩张窗口。当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。



import java.util.ArrayList;
import java.util.HashMap;

public class ZuiXiaoFuGaiZiChuan {

	public static void main(String[] args) {
		String a = "asdgasdewqewads";
		String b = "ads";
		Solution1 s = new Solution1();
		System.out.println(s.minWindow(a, b));
	}

}
class Solution1 {

	public String minWindow(String s, String t) {
		int slen = s.length();
		int tlen = t.length();
		if (slen == 0 || tlen == 0 || slen < tlen) {
			return "";
		}
		char[] sc = s.toCharArray();
		char[] tc = t.toCharArray();
		int[] si = new int[128];
		int[] ti = new int[128];
		for (char c : tc) {
			ti[c]++;
		}
		int minlen = slen + 1;
		int distance = 0;
		int right = 0;
		int left = 0;
		int begin = 0;
		while (right < slen) {
			if (ti[sc[right]] == 0) {
				// 在子串中不存在该字母
				right++;
				continue;
			}
			if (si[sc[right]] < ti[sc[right]]) {
				//精髓 避免串中出现的子串字母不是自己需要的
				// 串中该字母出现的次数小于子串出现次数
				distance++;
			}
			si[sc[right]]++;
			right++;
			while (distance == tlen) {
				//右移过程找全了子串 再左移找最小重复子串
				if (right - left < minlen) {
					//找到最小覆盖子串
					minlen = right - left;
					begin = left;
				}
				if (ti[sc[left]] == 0) {
					// 在子串中不存在该字母
					left++;
					continue;
				}
				if (si[sc[left]] == ti[sc[left]]) {
					//满足wihle时 必然si[sc[left]] >= ti[sc[left]]
					//所以si[sc[left]]--; 不能放在该条件中
					//移除需要的字符
					// 串中该字母出现的次数等于子串出现次数
					distance--;
				}
				si[sc[left]]--;
				left++;

			}
		}
		if(minlen == slen+1) {
			return "";
		}
		return s.substring(begin,begin + minlen);
    }
    
}
### Java 实现最小覆盖子串算法 对于最小覆盖子串问题,在字符串 `s` 中找到能覆盖字符串 `t` 所有字符的最小子串是一项挑战。下面展示了一个基于滑动窗口技术来解决问题的方法[^1]。 ```java import java.util.*; public class MinWindowSubstring { public String minWindow(String s, String t) { if (s == null || t == null || s.isEmpty() || t.isEmpty()) return ""; Map<Character, Integer> targetCount = new HashMap<>(); for (char c : t.toCharArray()) { targetCount.put(c, targetCount.getOrDefault(c, 0) + 1); } int requiredChars = targetCount.size(); int formed = 0; Map<Character, Integer> windowCounts = new HashMap<>(); int left = 0, right = 0; int[] ans = {-1, 0, 0}; while (right < s.length()) { char character = s.charAt(right); windowCounts.put(character, windowCounts.getOrDefault(character, 0) + 1); if (targetCount.containsKey(character) && windowCounts.get(character).intValue() == targetCount.get(character).intValue()) { formed++; } while (left <= right && formed == requiredChars) { character = s.charAt(left); if (ans[0] == -1 || right - left + 1 < ans[0]) { ans[0] = right - left + 1; ans[1] = left; ans[2] = right; } windowCounts.put(character, windowCounts.get(character) - 1); if (targetCount.containsKey(character) && windowCounts.get(character).intValue() < targetCount.get(character).intValue()) { formed--; } left++; } right++; } return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1); } } ``` 上述代码实现了滑动窗口方法,通过两个指针(`left`, `right`)维护当前考察的窗口范围,并利用哈希表记录目标字符串`t`中的字符频率以及当前窗口内的字符频率。当窗口内包含了足够的`t`中字符时尝试收缩左边界以优化解的空间大小[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值