开篇吐槽
谈到滑动窗口算法,我个人的感觉是想法很美好,现实很残酷,每次写滑动窗口的题,都是思路满满,代码慢慢,窗口的维护,真让人头大,那么多测试用例,就你不通过,头疼。。。
看了很多人的博客,发现了一套模板,希望你面对此类问题可以砍瓜切菜,如入无人之境,下面进入正题....
算法逻辑
滑动窗口算法本质就是维护窗口,不断更新。容易出错的地方就是窗口的维护,满足什么条件扩大窗口、什么条件缩小窗口,这虽然因题而异,但希望下面的框架能为你起到帮助....
public String slidingWindow(String s, String t) {
int size = 0; // 实际窗口大小
int need = 0; // 需要窗口大小
(1)计算需要的窗口大小
for(int i = 0; i < t.length(); i++) {
if(条件判断) need++;
}
for(int i = 0, j = 0; i < s.length(); i++) {
(2)实际窗口扩展
if(判断条件) size++;
(3)当实际窗口大小满足需要窗口大小时,开始收缩窗口
while(判断条件) {
计算答案
(4)实际窗口缩小
if(判断条件) size--;
j++;
}
}
return "";
}
这里指的窗口大小是根据条件计算得到的,并非i,j之间的距离
- 计算需要窗口大小【这一步很关键,它决定后面我们扩展和缩小窗口的具体判断条件,一定要想清楚】
- 实际窗口扩展
- 当实际窗口大小满足需要窗口大小时,开始收缩窗口【注意边界,防止溢出;注意最终答案的更新条件,避免漏判】
- 实际窗口缩小
题目自查
题目预览
给定两个字符串s和t。 返回s中包含t的所有字符的最短子字符串。
如果s中不存在符合条件的子字符串,则返回空字符串""。
如果s中存在多个符合条件的子字符串,返回任意一个。
注意: 对于t中重复字符,我们寻找的子字符串中该字符数量必须不少于t中该字符数量。
- 计算需要窗口大小:题目指出s中包含t的全部字符,那么我们计算t中字符的种类作为需要窗口的大小
- 实际窗口扩展:当s中的字符在t中并且所需的数量刚好等于最小数量时,这时扩展实际窗口的大小。为什么是刚好等于,而不是大于等于,因此大于等于会导致重复窗口重复扩展。
- 当实际窗口大小满足需要窗口大小时,开始收缩窗口
- 实际窗口缩小:当s中的字符在t中,减少对应的数量,如果数量小于需要的,缩小窗口
public String minWindow(String s, String t) {
char[] ch = s.toCharArray();
char[] ct = t.toCharArray();
int left = 0;
int right = ch.length+1;
int size = 0;
int need = 0;
int[][] pos = new int[2][128];
for(char c : ct) {
pos[0][c - 'A']++;
}
for(int i = 0; i < 128; i++) {
if(pos[0][i] > 0) need++;
}
for(int i = 0, j = 0; i < ch.length; i++) {
for(char c : ct) {
if(c == ch[i]) {
pos[1][ch[i] - 'A']++;
if(pos[1][ch[i] - 'A'] == pos[0][ch[i] - 'A']) {
size++;
}
break;
}
}
while(size == need && j <= i) {
if(right - left > i - j) {
right = i;
left = j;
}
for(char c : ct) {
if(c == ch[j]) {
pos[1][ch[j] - 'A']--;
if(pos[1][ch[j] - 'A'] < pos[0][ch[j] - 'A']) {
size--;
}
break;
}
}
j++;
}
}
return right - left == ch.length+1 ? "" : s.substring(left, right+1);
}
题目描述:
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
看了上面的题目,大家有没有发现这两道题很相似,只是换了种说法。
题目二要求我们找到最小替换条件的组最短字串,可是题目中表明经过替换每个字符出现的字数是相等的,因此最小的替换的次数是固定不变的,我们只需要将多的字符换成少的字符即可,即找到出现多的字符在字符串中的最小长度。
综上所述,题目二本质是再求最小覆盖字串,文字不好理解,看示例:
String s = "AAASFDFF"
对于字符串s来说,最小的替换次数一定是2(替换一个A和F),那么寻找最小替换字串长度就是找字符串s中字符串t="AF"的最小覆盖字串,因此题目完美解决。
代码如下:
public static int solution(String input) {
char[] str = input.toCharArray();
int size = str.length/4;
int ans = Integer.MAX_VALUE;
int[] dp = new int[26];
int need = 0;
int window = 0;
int[] count = new int[26];
for (char c : str) {
dp[c - 'A']++;
}
for (int d : dp) {
if(d > size) need++;
}
if(need == 0) return 0;
for (int i = 0, j = 0; i < str.length; i++) {
count[str[i] - 'A']++;
if(dp[str[i] - 'A'] > size && count[str[i] - 'A'] == dp[str[i] - 'A'] - size) {
window++;
}
while(window == need && j <= i) {
ans = Math.min(ans, i-j+1);
count[str[j] - 'A']--;
if(dp[str[j] - 'A'] > size && count[str[j] - 'A'] < dp[str[j] - 'A'] - size) {
window--;
}
j++;
}
}
return ans;
}
总结
- 面对此类问题一定要认真分析题目,确实计算窗口大小的条件
- 在计算最终结果时,一定要确保能找到所有的情况,避免漏判
- 题目中给解题代码不是最佳,大家请自行优化