import java.util.HashMap;
public class MinWindowSubstring {
//方法一:暴力法,枚举s中所有子串
public String minWindow(String s, String t) {
//定义最小字串,保存结果,初始为空字符串
String minSubString = "";
//定义一个HashMap 保存t中字符出现的频次
HashMap<Character, Integer> tCharFrequency = new HashMap<>();
//统计t中字符频次
for (int i = 0; i <t.length() ; i++) {
char c = t.charAt(i);
int count = tCharFrequency.getOrDefault(c,0);
tCharFrequency.put(c,count+1);
}
// 接下来在s中搜索覆盖子串
//遍历所有字符,作为当前子串的起始位置
for (int i = 0; i < s.length(); i++) {
//遍历i之后不小于t长度的位置,作为字串的结束位置
for (int j = i+t.length(); j <=s.length(); j++) {
//统计s字串中每个字符出现的频次
//定义一个HashMap 保存s字串中字符出现的频次
HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();
//统计t中字符频次
for (int k = i; k <j ; k++) {
char c = s.charAt(k);
int count = subStrCharFrequency.getOrDefault(c,0);
subStrCharFrequency.put(c,count+1);
}
//如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
if(check(tCharFrequency,subStrCharFrequency)&&(minSubString.equals("")||j-i<minSubString.length())){
minSubString = s.substring(i,j);
}
}
}
return minSubString;
}
//提炼一个方法,用于检查当前子串是否是一个覆盖t的子串
public boolean check(HashMap<Character,Integer> tFreq,HashMap<Character,Integer> subStringFreq){
//遍历t中的字符,看在substring中是否包含
for (char c:tFreq.keySet()) {
if(subStringFreq.getOrDefault(c,0)<tFreq.get(c)){
return false;
}
}
return true;
}
//方法二:滑动窗口
public String minWindow2(String s, String t) {
//定义最小字串,保存结果,初始为空字符串
String minSubString = "";
//定义一个HashMap 保存t中字符出现的频次
HashMap<Character, Integer> tCharFrequency = new HashMap<>();
//统计t中字符频次
for (int i = 0; i <t.length() ; i++) {
char c = t.charAt(i);
int count = tCharFrequency.getOrDefault(c,0);
tCharFrequency.put(c,count+1);
}
//定义左右指针指向滑动窗口的起始和结束位置
int start = 0;
int end = t.length();
while (end<=s.length()){
//定义一个HashMap 保存s字串中字符出现的频次
HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();
//统计t中字符频次
for (int k = start; k <end ; k++) {
char c = s.charAt(k);
int count = subStrCharFrequency.getOrDefault(c,0);
subStrCharFrequency.put(c,count+1);
}
//如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
if(check(tCharFrequency,subStrCharFrequency)){
if((minSubString.equals("")||end-start<minSubString.length())){
minSubString = s.substring(start,end);
}
//只要是覆盖子串,就移动初始位置
start++;
}else{
//如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
end++;
}
}
return minSubString;
}
//方法三:滑动窗口优化
public String minWindow3(String s, String t) {
//定义最小字串,保存结果,初始为空字符串
String minSubString = "";
//定义一个HashMap 保存t中字符出现的频次
HashMap<Character, Integer> tCharFrequency = new HashMap<>();
//统计t中字符频次
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
int count = tCharFrequency.getOrDefault(c, 0);
tCharFrequency.put(c, count + 1);
}
//定义左右指针,指向滑动窗口的起始和结束位置
int start = 0,end = 1;
//定义一个hashmap,保存s子串中字符出现的频次
HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();
while (end<=s.length()){
//end增加之后,新增的字符
char newChar = s.charAt(end-1);
if(tCharFrequency.containsKey(newChar)){
subStrCharFrequency.put(newChar,subStrCharFrequency.getOrDefault(newChar,0)+1);
}
//如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
while (check(tCharFrequency,subStrCharFrequency)&&start<end){
if((minSubString.equals("")||end-start<minSubString.length())){
minSubString = s.substring(start,end);
}
//对要删除的字符,频次减1
char removeChar = s.charAt(start);
if(tCharFrequency.containsKey(removeChar)){
subStrCharFrequency.put(removeChar,subStrCharFrequency.getOrDefault(removeChar,0)-1);
}
//只要是覆盖子串,就移动初始位置
start++;
}
//如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
end++;
}
return minSubString;
}
//方法四:进一步优化
public String minWindow4(String s, String t) {
//定义最小字串,保存结果,初始为空字符串
String minSubString = "";
//定义一个HashMap 保存t中字符出现的频次
HashMap<Character, Integer> tCharFrequency = new HashMap<>();
//统计t中字符频次
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
int count = tCharFrequency.getOrDefault(c, 0);
tCharFrequency.put(c, count + 1);
}
//定义左右指针,指向滑动窗口的起始和结束位置
int start = 0,end = 1;
//定义一个hashmap,保存s子串中字符出现的频次
HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();
//定义一个”子串贡献值“变量,统计t中的字符在子串中出现了多少
int charCount = 0;
while (end<=s.length()){
//end增加之后,新增的字符
char newChar = s.charAt(end-1);
if(tCharFrequency.containsKey(newChar)){
subStrCharFrequency.put(newChar,subStrCharFrequency.getOrDefault(newChar,0)+1);
//如果子串中频次小于t中频次,当前频次有贡献
if(subStrCharFrequency.get(newChar)<=tCharFrequency.get(newChar)){
charCount++;
}
}
//如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
while (charCount==t.length()&&start<end){
if((minSubString.equals("")||end-start<minSubString.length())){
minSubString = s.substring(start,end);
}
//对要删除的字符,频次减1
char removeChar = s.charAt(start);
if(tCharFrequency.containsKey(removeChar)){
subStrCharFrequency.put(removeChar,subStrCharFrequency.getOrDefault(removeChar,0)-1);
//如果子串中的频次如果不够t中的频次,贡献值减少
if(subStrCharFrequency.get(removeChar)<tCharFrequency.get(removeChar)){
charCount--;
}
}
//只要是覆盖子串,就移动初始位置
start++;
}
//如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
end++;
}
return minSubString;
}
public static void main(String[] args) {
String s = "ADOBECODEBANC", t = "ABC";
MinWindowSubstring minWindowSubstring = new MinWindowSubstring();
String s1 = minWindowSubstring.minWindow4(s, t);
System.out.println(s1);
}
}