关于使用滑动窗口解决数组的一系列问题

在使用滑动窗口之前,我们需要知道什么是滑动窗口,它又能帮助我们解决什么样的问题?

为了理解滑动窗口是什么,我们先来看一个简单的例子,难度指数:简单

这道题在leetcode上也能找到:209 Mininum Size Subarray Sum

//难度:*
/*
209 Minimum Size Subarray Sum
在字符串中找到满足条件的最小子字符串
给定一个数s和一个整形数组,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>=s,返回这个最短子数组的长度
 */

public class MinimumSizeSubarraySum {
    //滑动窗口求最小子数组
    //时间复杂度:O(n)
    //空间复杂度:O(1)
    public static int minSubArrayLen(int s, int[] arr){
        int l = 0, r = -1;//arr[l...r]是我们的滑动窗口
        int sum = 0;//子数组的和
        int res = arr.length + 1;//子数组的最短长度
        
    while( l < arr.length ){
        if(sum < s && r+1 < arr.length){
            r++;
            sum += arr[r];
        }
        else{
            sum -= arr[l];
            l++;
        }
        if( sum >= s)
            res = Math.min(res, r-l+1);
    }
if(res == arr. length+ 1) return 0; return res; } public static void main(String[] args) { int[] nums = { 2, 3, 1, 2, 4, 3}; int s = 7; System. out.println( minSubArrayLen(s , nums)); }}

    这就是一个典型的滑动窗口的应用,arr[l..r]就是一个窗口,如果sum即子数组的和<s,则右边界++,将下一个数加入进窗口;如果sum>=s,则左边界++,将子数组第一个数踢出窗口。最后,每一次遍历的时,都判断一下子数组是否满足题目要求,即sum>=s,如果是,则将其与现在的res相比较,较小值存进res。

    俺懒得画那啥图,没能体会其中思想的可以在草稿纸上画一下,多体会体会。

现在我们来看看下一个例子,难度:一般

//难度:**
/*
3 Longest Substring Without Repeating Characters
在一个字符串中寻找没有重复字母的最长子字符串,A和a是不同的
 */

public class LongestSubstring {
    //使用滑动窗口解决
    /*
    怎么判定有没有重复字符呢?使用一个freq[256]来记录字符出现的频率
     */
    public static int longestSubstring(String s){
        int[] freq = new int[256];//初始化都是0,存储的是滑动窗口中的字符的频率
        int l = 0, r = -1;//滑动窗口s[l...r]
        int res = 0;//滑动窗口的长度
        while( l<s.length() ){
            if( r + 1< s.length() && freq[s.charAt(r+1)] == 0 ){
                r++;
                freq[s.charAt(r)]++;
            }
            else{
                freq[s.charAt(l)]--;
                l++;
            }
            res = Math.max( res, r-l+1);
        }
        return res;
    }

    public static void main(String[] args) {
        String s = "abcabcdbb";
        System.out.println(longestSubstring(s));
    }
}

可以看出,这道题和上一道思想上没有不同,都是创建一个滑动窗口来进行遍历,不过要注意的是这里使用的是一个freq[256]的整形数组来记录滑动窗口中字符的频率,而右边界r++,左边界l++的判断条件也不一样。可以总结出一个模板:

int l = 0, r = -1;
while( l<s.length() ){
    if(){
    r++;
    }else{
    l++;
    }
}

创建一个滑动窗口,满足条件r++,否则l++;所以我们在这类大数组里找小数组的问题,都可以用这种模板,这时我们需要考虑的只是判断条件的不同。接下来,让我们看2个一样运用了滑动窗口的例子,这一次的例子比较难,我在这里先给出和两个题目,希望大家能独立完成,实在不行再来看下面的答案。

//难度:***
/*
438 Find All Anagrams in a String
给定一个字符串s和一个非空字符串p,找出p中的所有是s 的 anagrans字符串的子串,返回这些子串的起始索引
s = "cbaebabacd" p="abc" ,返回[0,6]
s = "abab" p = "ba" , 返回[0,1,2]
 */
//难度:***
/*
76 Mininum Window Substring
给定一个字符串S,一个字符串T,在S中寻找最短的子串,包含T中所有的字符
S = "ADOBECODEBANC"    T = "ABC"
结果为"BANC"
 */

第一个问题的答案:

public class FindinString {
    public static Integer[] FindinString(String s, String p){
        int[] freq = new int[256];//存储p字符频率
        for(int i=0;i<p.length();i++){
            freq[p.charAt(i)]++;
        }
        int l = 0, r = -1;
        Vector<Integer> vec = new Vector<>();
        while( l<s.length() ){
            if( r+1<s.length() && freq[s.charAt(r+1)]!=0 ){
                freq[s.charAt(r+1)]--;
                r++;
            }else{
                freq[s.charAt(l)]++;
                l++;
            }
            if( r-l+1 == p.length()){//当滑动窗口的长度和字符串p的长度相等时就找到了
                vec.add(l);
            }
        }
        Integer[] arr = vec.toArray(new Integer[vec.size()]);
        return arr;
    }

    public static void show(Integer[] arr){
        System.out.print("[");
        for(int i=0;i<arr.length-1;i++)
            System.out.print(arr[i] + ",");
        System.out.print(arr[arr.length-1]+"]");
        System.out.println();
    }

    public static void main(String[] args) {
        String s = "cbaebabacd";
        String p = "abc";
        Integer[] arr = FindinString(s, p);
        show(arr);
        s = "abab";
        p = "ab";
        arr = FindinString(s , p);
        show(arr);
    }
}

第二个问题的答案:

public class MininumWindowSubstring {
    public static String MininumWimow(String S, String T){
        int[] freq = new int[256];
        for( int i=0; i<T.length(); i++){
            freq[T.charAt(i)]++;
        }
        String str = new String();
        int l = 0, r = -1;
        int count = 0;
        int res = S.length()+1;
        while( l<S.length() ){
            if( r+1<S.length() && count<T.length() ){
                r++;
                if( freq[S.charAt(r)]>0)
                    count++;
                freq[S.charAt(r)]--;
            }else{
                if(freq[S.charAt(l)]>=0)
                    count--;
                freq[S.charAt(l)]++;
                l++;
            }
            if( count == T.length() ){
                if( r-l+1 < res )
                    str = S.substring(l , r+1);
            }
        }
        return str;
    }

    public static void main(String[] args) {
        String s = "ADOBECODEBANC";
        String t = "ABC";
        String str = MininumWimow( s, t);
        System.out.println(str);
    }
}
这里面的判断条件有不同的地方,但是模板还是一样的,都使用了freq[256]这个辅助数组,大家可以好好体会一下,不懂的可以在下面提问,看到了会回答的。





猜你喜欢

转载自blog.csdn.net/qq_40006051/article/details/80718041
今日推荐