[スライディング ウィンドウ] スライディング ウィンドウ テンプレート、小さなアルゴリズムの問題をスライドしてスライドします。

まず第一に、全員が私たちのスローガンを叫びました。「テンプレートに従い、スライド ウィンドウについて心配する必要はありません。」

1. 引き違い窓とは何ですか?

スライディングウィンドウ アルゴリズムは、ダブル ポインター アルゴリズムの特定のアルゴリズム モデルであり、特定の条件下で最大または最小の文字列、特定の配列、文字シーケンスやその他の関連する問題を見つけるためによく使用されます。これも非常に単純です。 : 本来は二重ループの入れ子によって解決する必要がある問題が、二重ポインタによって解決されるため、時間計算量が大幅に削減されます (一般に、O(n^2) の時間計算量は O( n))

2.引き違い窓のフレームとテンプレート

スライディング ウィンドウの問題を解決する際の難点

LeetCode の質問に取り組むときに、多くの友人がこのように感じるかもしれません。この質問はスライディング ウィンドウを使用して解決されているようですが、どうやって解決しますか? すると、左右の2つのポインタが列挙され、途方に暮れていますが、これは主にスライディング ウィンドウ アルゴリズム全体の実行プロセスに慣れていないことが原因です。この問題の解決方法はわかっていますが、2 日後にこのトピックをもう一度見てみると、スライディング ウィンドウ アルゴリズムのいくつかの特定の手順についてのインスピレーションが不足しているため、このトピックについてはまだ迷っています。ウィンドウアルゴリズム、スライディングウィンドウのアルゴリズムモデルが誕生しました~

スライディング ウィンドウ フレームワークを使用する利点

スライディング ウィンドウ フレームワークの利点は、次の点から説明できます: 1. スライディング ウィンドウ フレームワークを使用すると、質問全体の難易度が軽減され、特定の困難な点でインスピレーションが得られます。核となるアイデアを考えることにもっとエネルギーを注ぎ、質問をブラッシュアップする効率を高めます。3. 開発にテンプレートを使用する このトピックを振り返ると、そのトピックの難しい点に直接記憶が固定され、記憶がより深くなります。

スライディングウィンドウのフレームテンプレートは次のとおりです

最長のテンプレート


テンプレートの実装アイデアは次のとおりです。 スライディング ウィンドウのテンプレートを使用する場合、最長および最短の文字シーケンスまたは文字列を見つけるための 2 つの異なる実装アイデアとテンプレート コードを提供します。最長の相関関係を見つけるための実装アイデア:

左右のポインタ (LR) は初期位置にあり、R ポインタは徐々に右に円を描くようにスライドします。各スライド プロセス中、スライディング ウィンドウ内の要素がスライディング ウィンドウの要件を満たしている場合、R ポインタはスライドし続けます。右に移動するとウィンドウが拡大され、継続的に更新されます。 最適な結果。ウィンドウ内の要素が条件を満たさない場合、L ポインタはウィンドウを右に縮小し続け、R が条件に達するまでプロセス内で結果が継続的に更新されます。終わり

初始化 left,right,result,bestResult
while("右指针没有到结尾"){
    窗口扩大,加入right对应元素,更新当前result
    while("result不满足要求"){
        窗口缩小,移除left对应元素,left右移
    }
    更新最优结果bestResult
    right++;
}
返回bestResult

最短のテンプレート

実装アイデア:

左右のポインタ (LR) は最初は始点にあり、R は徐々に右にスライドします。スライドの各プロセス中に、ウィンドウ内の要素が条件を満たしていれば、L はウィンドウを右に縮小し、最小値を継続的に更新します。ウィンドウ内の要素が条件を満たさない場合、R は文字列の末尾に達するまでウィンドウを右に拡張し続けます。

初始化 left,right,result,bestResult
while("右指针没有到结尾"){
    窗口扩大,加入right对应元素,更新当前result
    while("result满足要求"){
        更新最优结果bestResult
        窗口缩小,移除left对应元素,left右移
    }
    right++;
}
返回bestResult

3. 実戦分析

3.1 およびターゲット以上の最短のサブ配列

トピックの説明

問題解決のアイデア

上記のテンプレートのロジックに従って、次の問題解決のアイデアを提供します。この質問は最短の部分配列を探しているため、2 つのポインター (LR) を定義します。条件が満たされない場合は、上記のテンプレートを適用します。 、R は右に移動し続けます。条件が満たされると、L ポインタが右に移動してウィンドウ内の要素の数を継続的に減らし、最終的に求められる配列の数の最小値を取得します。

サンプルコード

コード例を示します。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //设置最小个数
        int count=0;
        int left=0;
        int right=0;
        int sum=0;
        int minLength=Integer.MAX_VALUE;
        //进行遍历
        while(right<nums.length){
            sum+=nums[right];
            count++;
            while(sum>=target){
                if(count<minLength){
                    minLength=count;
                }
                sum-=nums[left];
                 left++;
                count--;
            }
            right++;
        }
        if(minLength==Integer.MAX_VALUE){
            return 0;
        }
        return minLength;
    }
}

3.2 最小カバー部分文字列

トピックの説明

問題解決のアイデア

このテスト問題の難しさに怯える必要はありません。テンプレートに従えば、ウィンドウをスライドさせるのに問題はないと常に信じなければなりません。OK, この質問に対する解決策について話しましょう: この質問は、ターゲットの部分文字列をカバーするために必要な部分文字列の数を見つけることです。まず、ハッシュマップを使用して、ターゲットの部分文字列に必要な文字と、各文字に必要な文字数を比較します。数値が走査され、LR の 2 つのポインターが定義されます。このトピックはまだ最小の部分文字列を見つけることであるため、走査する文字列が要件を満たさない場合、R ポインターは継続的に右に走査し、別のハッシュマップを使用します。ウィンドウに要素を格納します。トラバースしたポインターがターゲット文字列に必要な関連文字の数を満たせる場合、L ポインターを使用してウィンドウ内の要素の数を右に減らし続け、続行します。 R が文字列の最後の要素に到達するまでこのプロセスを繰り返します

サンプルコード

サンプルコードを示します。

class Solution {
    public String minWindow(String s, String t) {
        //创建双指针
        int left=0;
        int right=0;
        //定义两个hashmap
        HashMap<Character,Integer>map1=new HashMap<>();
        HashMap<Character,Integer>map2=new HashMap<>();
        int start=0;
        int len=Integer.MAX_VALUE;
        //设置要求
        int need=0;
        //设置满足情况
        int satisfy=0;
        //遍历要求
        for(char c: t.toCharArray()){
            map1.put(c,map1.getOrDefault(c,0)+1);
            if(map1.get(c)==1){
                need++;
            }
        }
        //进行遍历
        int n=s.length();
        while(right<n){
            map2.put(s.charAt(right),map2.getOrDefault(s.charAt(right),0)+1);
            if(map2.get(s.charAt(right)).equals(map1.get(s.charAt(right)))){
                satisfy++;
            }
            //优化结果
            while(satisfy==need){
                if(len>right-left+1){
                    len=right-left+1;
                    start=left;
                }
                char rm=s.charAt(left);
                if(map1.containsKey(rm)){
                    if(map1.get(rm).equals(map2.get(rm)))
                    satisfy--;
                }
                map2.put(rm,map2.get(rm)-1);
                left++;
            }
            right++;
        }
        if(len==Integer.MAX_VALUE){
            return "";
        }
        return s.substring(start,start+len);
    }
}

3.3 弦の配置

トピックの説明

問題解決のアイデア

この質問は上記の質問と似ています。また、2 つのハッシュマップが必要です。1 つは、標準文字と各文字に対応する要素の数を保存するために使用されます。前の質問と同じ方法で調べられ、最後に 1 つの違いがあります。つまり、取得した最小値と要件を満たす長さを最終的に比較し、等しい場合は TRUE を返し、等しくない場合は FALSE を返します。

サンプルコード

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //创建双指针
        int left=0;
        int right=0;
        //创建hashmap
        int[]nums1=new int[26];
        int[]nums2=new int[26];
        int maxLength=Integer.MAX_VALUE;
        int ask=0;
        int satisfy=0;
        //遍历要求
        for(int i=0;i<s1.length();++i){
            nums1[s1.charAt(i)-'a']++;
            if(nums1[s1.charAt(i)-'a']==1){
                ask++;
            }
        }
        //进行遍历
        while(right<s2.length()){
            nums2[s2.charAt(right)-'a']++;
            if(nums2[s2.charAt(right)-'a']==nums1[s2.charAt(right)-'a']){
                satisfy++;
            }
            while(satisfy==ask){
                if(right-left+1<maxLength){
                    maxLength=right-left+1;
                }
                if(nums2[s2.charAt(left)-'a']==(nums1[s2.charAt(left)-'a'])){
                    satisfy--;
                }
                nums2[s2.charAt(left)-'a']--;
                left++;
            }
            right++;
        }
        return maxLength==s1.length()?true:false;
    }
}

3.4 文字列内のすべてのアナグラムを検索する

トピックの説明

問題解決のアイデア

これも前の質問と同様の解決策ですが、唯一の違いは、前の質問の解決策に基づいて各トラバーサルの minLength をカウントし、要件を満たしている場合は、このセットをリストに直接追加できることです。

サンプルコード

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        //创建list
        List<Integer>list=new ArrayList<>();
        //基本类似的解题思路:
        //创建双指针
        int left=0;
        int right=0;
        //创建两个哈希表
         int[]nums1=new int[26];
        int[]nums2=new int[26];
        int ask=0;
        int satisfy=0;
        //遍历要求
        for(int i=0;i<p.length();++i){
            nums1[p.charAt(i)-'a']++;
            if(nums1[p.charAt(i)-'a']==1){
                ask++;
            }
        }
        //遍历实际的字符串
        while(right<s.length()){
            nums2[s.charAt(right)-'a']++;
            if(nums1[s.charAt(right)-'a']==nums2[s.charAt(right)-'a']){
                satisfy++;
            }
            //优化结果
            while(satisfy==ask){
                if(right-left+1==p.length()){
                    list.add(left);
                }
                if(nums2[s.charAt(left)-'a']==(nums1[s.charAt(left)-'a'])){
                    satisfy--;
                }
                nums2[s.charAt(left)-'a']--;
                left++;
            }
            right++;
        }
        return list;
    }
}

3.5 繰り返し文字を含まない最長の部分文字列

トピックの説明

問題解決のアイデア

この質問は前の質問とは比較的異なります。スライディング ウィンドウの種類の観点から見ると、これは最大のスライディング ウィンドウのサイズです。この質問の問題解決プロセスを詳しく説明します。ストアするハッシュ テーブルを作成します。要素を毎回走査し、走査するダブル ポインターを作成します。R ポインターは右への走査を続けます。要素が走査されるたびに、その要素をハッシュ テーブルに追加します。ウィンドウ内の要素の数が増加すると、その要素はハッシュ テーブルに追加されます。トピックの要件を満たしていない場合 (要素が 2 回出現する場合)、質問の要件が満たされるまで L ポインタを右に移動し続け、最終的に最大 maxLength を返します。

サンプルコード

class Solution {
    public int lengthOfLongestSubstring(String s) {
        
        //创建哈希表
        HashMap<Character,Integer>map=new HashMap<>();
        //定义双指针
        int left=0;
        int right=0;
        //定义总长度
        int n=s.length();
        int maxLength=0;
        while(right<n){
            map.put(s.charAt(right),map.getOrDefault(s.charAt(right),0)+1);
            while(map.get(s.charAt(right))>1){
                 map.put(s.charAt(left),map.get(s.charAt(left))-1);
                 left++;
            }
            if(right-left+1>maxLength){
                maxLength=right-left+1;
            }
            right++;
        }
       return maxLength; 
    }
}

3.6 試験の最高難易度

トピックの説明

問題解決のアイデア

この問題も最小のウィンドウを見つけることであり、問​​題解決のアイデアは比較的明確です。指定された k 個の普遍値を使用して、TRUE または FALSE の 2 つの部分の間のギャップを埋めると、R ポインタは右に移動し続けます。 . k がギャップ If を満たすことができない場合、L は K が満たされるまで右に移動し、R ポインターが文字列の末尾に到達するまで移動します。

サンプルコード

class Solution {
    public int maxConsecutiveAnswers(String answerKey, int k) {
        //核心思路:使用提供的k的个万能值,来填补TRUE和FALSE之间的空缺值
        //创建两个指针
        int left=0;
        int right=0;
        //创建计数器
        int countT=0;
        int countF=0;
        int n=answerKey.length();
        int maxLength=0;
        //进行遍历循环
        while(right<n){
            char c=answerKey.charAt(right);
            
            if(c=='T'){
                countT++;
            }else{
                countF++;
            }
            //不符合的情况
            while(countT>k&&countF>k){
                char d=answerKey.charAt(left);
                //进行遍历
                if(d=='T'){
                    countT--;
                }else{
                    countF--;
                }
                left++;
            }
            if(right-left+1>maxLength){
                maxLength=right-left+1;
            }
            right++;
        }
        return maxLength;

    }
}

おすすめ

転載: blog.csdn.net/m0_65431718/article/details/130730847