可能可以参考的模板写法:
public class Solution {
public String minWindow(String s, String t) {
// 起始的时候,都位于 0,同方向移动
int left = 0;
int right = 0;
// 根据题意设计状态变量
while (right < len) {
// 在 right 右移的过程中维护状态变量的定义
if () {
// 维护状态变量的定义
}
// 右边界右移 1 格
right++;
// 状态变量满足一定条件,窗口是一个可行解
while ( ) {
// 在 left 右移的过程中维护状态变量的定义
if () {
// 维护状态变量的定义
}
// 左边界右移 1 格
left++;
}
}
return 需要的结果变量;
}
}
使用字符频数数组,用加法:
加法 continue
的写法;
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
// 字符频数数组,用哈希表也可以,用 128 是测试测出来的,后台数据的字符 ascii 值最多到 `z`(122)
int[] winFreq = new int[128];
int[] tFreq = new int[128];
// 滑动窗口内部不同字符的个数
int distance = 0;
char[] charArrayS = s.toCharArray();
char[] charArrayT = t.toCharArray();
for (char c : charArrayT) {
tFreq[c]++;
}
int minLen = sLen + 1;
// 与 minLen 对应的起始位置的下标
int begin = 0;
int left = 0;
int right = 0;
// 以下才是滑动窗口的核心逻辑
// 循环不变量:s[left..right) 包含 T 中所有的字符
while (right < sLen) {
if (tFreq[charArrayS[right]] == 0) {
right++;
continue;
}
// 只关心在 T 中出现的字符
if (winFreq[charArrayS[right]] < tFreq[charArrayS[right]]) {
distance++;
}
winFreq[charArrayS[right]]++;
// 看完了 right 位置的字符,并且统计了相关信息以后,指针右移
right++;
while (distance == tLen) {
if (tFreq[charArrayS[left]] == 0) {
left++;
continue;
}
// 只关心在 T 中出现的字符
if (winFreq[charArrayS[left]] == tFreq[charArrayS[left]]) {
distance--;
}
winFreq[charArrayS[left]]--;
// 计算最小长度,只要在 left++ 之前都行
// 把区间定义成左闭右开的原因是,长度值 = right - left 不用 + 1
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
}
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
char[] charArrayS = s.toCharArray();
char[] charArrayT = t.toCharArray();
int[] winFreq = new int[128];
int[] tFreq = new int[128];
// 窗口内包含 T 的字符的个数(字符频数大于 T 中的字符频数时,不再增加)
int distance = 0;
for (char charT : charArrayT) {
tFreq[charT]++;
}
int begin = 0;
int minLen = sLen + 1;
int left = 0;
int right = 0;
// [left, right) 每一轮开始之前,right 这个下标左边的元素我们都看过了
while (right < sLen) {
// 只关心 T 中存在的字符
char rightChar = charArrayS[right];
if (tFreq[rightChar] == 0) {
right++;
continue;
}
if (winFreq[rightChar] < tFreq[rightChar]) {
distance++;
}
winFreq[rightChar]++;
// 到了下一轮
right++;
while (distance == tLen) {
// 这里位置还可以靠后,只要在 left 之前都是没有问题的
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
char leftChar = charArrayS[left];
if (tFreq[leftChar] == 0) {
left++;
continue;
}
if (winFreq[leftChar] == tFreq[leftChar]) {
distance--;
}
winFreq[leftChar]--;
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
public static void main(String[] args) {
Solution solution = new Solution();
String S = "ADOBECODEBANC";
String T = "ABC";
String res = solution.minWindow(S, T);
System.out.println(res);
}
}
加法,不 continue
的写法;
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
int[] tFreq = new int[128];
int[] winFreq = new int[128];
int distance = 0;
for (char tChar : t.toCharArray()) {
tFreq[tChar]++;
}
int left = 0;
int right = 0;
int minLen = sLen + 1;
int start = 0;
char[] sCharArray = s.toCharArray();
while (right < sLen) {
char rightChar = sCharArray[right];
if (tFreq[rightChar] > 0) {
if (winFreq[rightChar] < tFreq[rightChar]) {
distance++;
}
}
winFreq[rightChar]++;
right++;
while (distance == tLen) {
char leftChar = sCharArray[left] ;
if (tFreq[leftChar] > 0) {
if (winFreq[leftChar] == tFreq[leftChar]) {
distance--;
}
winFreq[leftChar]--;
}
if (right - left < minLen) {
minLen = right - left;
start = left;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(start, start + minLen);
}
}
加法,用哈希表
import java.util.HashMap;
import java.util.Map;
public class Solution3 {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
Map<Character, Integer> winFreq = new HashMap<>();
Map<Character, Integer> tFreq = new HashMap<>();
for (Character c : t.toCharArray()) {
tFreq.put(c, tFreq.getOrDefault(c, 0) + 1);
}
int minLen = sLen + 1;
int begin = 0;
int left = 0;
int right = 0;
int distance = 0;
while (right < sLen) {
Character rightChar = s.charAt(right);
if (tFreq.containsKey(rightChar)) {
// winFreq.get(rightChar) == null 这一步容易忽略
if (winFreq.get(rightChar) == null || winFreq.get(rightChar) < tFreq.get(rightChar)) {
distance++;
}
winFreq.put(rightChar, winFreq.getOrDefault(rightChar, 0) + 1);
}
right++;
while (distance == tLen) {
if (right - left < minLen) {
begin = left;
minLen = right - left;
}
Character leftChar = s.charAt(left);
if (tFreq.containsKey(leftChar)) {
// 注意:包装类型使用 equals
if (winFreq.get(leftChar).equals(tFreq.get(leftChar))) {
distance--;
}
winFreq.put(leftChar, winFreq.getOrDefault(leftChar, 0) - 1);
}
left++;
}
}
return minLen == sLen + 1 ? "" : s.substring(begin, begin + minLen);
}
}
数组,减法
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
int[] tFreq = new int[128];
// T 滑动窗口内部还差多少个「不同」字符能覆盖 T
int distance = tLen;
char[] charArrayS = s.toCharArray();
char[] charArrayT = t.toCharArray();
for (char c : charArrayT) {
tFreq[c]++;
}
int minLen = sLen + 1;
// 与 minLen 对应的起始位置的下标
int begin = 0;
int left = 0;
int right = 0;
// 以下才是滑动窗口的核心逻辑
// 循环不变量:s[left..right) 包含 T 中所有的字符
while (right < sLen) {
// 只关心在 T 中出现的字符
if (tFreq[charArrayS[right]] > 0) {
distance--;
}
tFreq[charArrayS[right]]--;
// 看完了 right 位置的字符,并且统计了相关信息以后,指针右移
right++;
while (distance == 0) {
// 把区间定义成左闭右开的原因是,长度值 = right - left 不用 + 1
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
// 只关心在 T 中出现的字符
tFreq[charArrayS[left]]++;
if (tFreq[charArrayS[left]] > 0) {
distance++;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
}
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
char[] charArrayS = s.toCharArray();
char[] charArrayT = t.toCharArray();
int distance = 0;
int begin = 0;
int minLen = sLen + 1;
int[] tFreq = new int[128];
for (char c : charArrayT) {
tFreq[c]++;
}
for (int left = 0, right = 0; right < sLen; right++) {
tFreq[charArrayS[right]]--;
if (tFreq[charArrayS[right]] >= 0) {
distance++;
}
while (distance == tLen) {
if (minLen > right - left + 1) {
minLen = right - left + 1;
begin = left;
}
tFreq[charArrayS[left]]++;
if (tFreq[charArrayS[left]] > 0) {
distance--;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
}
哈希表,用减法
import java.util.HashMap;
import java.util.Map;
public class Solution {
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
Map<Character, Integer> tFreq = new HashMap<>();
char[] charArrayT = t.toCharArray();
for (char c : charArrayT) {
tFreq.put(c, tFreq.getOrDefault(c, 0) + 1);
}
int distance = tLen;
int minLen = sLen + 1;
int left = 0;
int right = 0;
int begin = 0;
char[] charArrayS = s.toCharArray();
while (right < sLen) {
Integer rightFreq = tFreq.get(charArrayS[right]);
if (rightFreq == null) {
right++;
continue;
}
if(rightFreq > 0){
distance--;
}
tFreq.put(charArrayS[right], rightFreq - 1);
right++;
while (distance == 0) {
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
Integer leftFreq = tFreq.get(charArrayS[left]);
if (leftFreq == null) {
left++;
continue;
}
leftFreq++;
tFreq.put(charArrayS[left], leftFreq);
if (leftFreq > 0) {
distance++;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
public static void main(String[] args) {
Solution solution = new Solution();
String S = "ADOBECODEBANC";
String T = "ABC";
String minWindow = solution.minWindow(S, T);
System.out.println(minWindow);
}
}
参考资料:
- https://www.cnblogs.com/grandyang/p/4340948.html
比较麻烦的加法:
public class Solution {
// 最新版代码:加法
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
// 字符频数数组,用哈希表也可以,用 128 是测试测出来的,后台数据的字符 ascii 值最多到 `z`(122)
int[] winFreq = new int[128];
int[] tFreq = new int[128];
// 滑动窗口内部不同字符的个数
int winCount = 0;
// T 中不同字符的个数
int tCount = 0;
char[] charArrayS = s.toCharArray();
char[] charArrayT = t.toCharArray();
for (char c : charArrayT) {
if (tFreq[c] == 0) {
tCount++;
}
tFreq[c]++;
}
int minLen = sLen + 1;
// 与 minLen 对应的起始位置的下标
int begin = 0;
int left = 0;
int right = 0;
// 以下才是滑动窗口的核心逻辑
// 循环不变量:s[left..right) 包含 T 中所有的字符
while (right < sLen) {
// 只关心在 T 中出现的字符
if (tFreq[charArrayS[right]] == 0) {
right++;
continue;
}
winFreq[charArrayS[right]]++;
if (winFreq[charArrayS[right]] == tFreq[charArrayS[right]]) {
winCount++;
}
// 看完了 right 位置的字符,并且统计了相关信息以后,指针右移
right++;
while (winCount == tCount) {
// 只关心在 T 中出现的字符
if (tFreq[charArrayS[left]] == 0) {
left++;
continue;
}
winFreq[charArrayS[left]]--;
if (winFreq[charArrayS[left]] < tFreq[charArrayS[left]]) {
winCount--;
}
// 计算最小长度,只要在 left++ 之前都行
// 把区间定义成左闭右开的原因是,长度值 = right - left 不用 + 1
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
left++;
}
}
if (minLen == sLen + 1) {
return "";
}
return s.substring(begin, begin + minLen);
}
}