资讯专栏INFORMATION COLUMN

[leetcode] Minimum Window Substring

Pines_Cheng / 660人阅读

摘要:使用而不是因为我们需要的是最值,中间值我们不在乎,所以一次收敛到最小。下面来三个需要查重并且记录上次出现的位置,选择以为例,走到用做检查,发现出现过,把移到的下一个。是上个题目的简易版,或者特殊版。

这里聊一聊解一类问题,就是满足某一条件的substring最值问题。
最开始我们以Minimum Window Substring为例,并整理总结leetcode里所有类似题目的通解。

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
S = "ADOBECODEBANC" , T = "ABC", Minimum window is "BANC".

我们要把问题拆解成三个字问题:唯一的表示substring, 检查substring是否符合要求,记录最值。
1.
如何唯一的表示一个substring, 有两种方法,一种用头尾指针,一种用尾指针加substring长度。
2.
检查substring是否符合要求,这道题目要求包含T中的每个字母,所以我们需要一个counter来记录T里每个character出现的次数,空间O(26) O(128) O(256) 都行, 可以和面试官讨论。类似题目唯一变化的就是这个部分,可以是每个字符只能出现一次,最多出现两个不同字符,每个字符最多出现K次等等。
3.
记录最值。根据扫描方法的不同,长度有时为r-l,此时r不包含在substring里,有时为r-l+1,此时包含在substring里。

这篇博客主要讲第二个子问题,如何判断substring是否符合要求。以Minimum Window Substring为例。

public class Solution {
    public String minWindow(String s, String t) {
        // empty check
        if(s == null || t == null || s.length() == 0 || t.length() == 0) return "";
        // initial counter for valid check
        int[] counter = new int[128];
        for(char c : t.toCharArray()){
            counter[c]++;
        }
        // variables help represent substring and min/max
        char[] chs = s.toCharArray();
        // 变量名解释,i表示头即先开始扫描的指针,j表示尾。i,j对counter的操作相反,一个加一个减或者一个减一个加。 
        // 上面的counter记录每个字符出现次数,count表示substring里满足条件的字符出现总次数(可以从0加到count,也可以从count减少到0.
        int j = 0, i=0, minLen = Integer.MAX_VALUE, start = 0, count = t.length();
        
        //这里可以使用for和while两种循环,个人偏向for, 因为i指针要一次走一个字符遍历完整个string.
        for( ; i < chs.length; i++){      //逐个扫描
            // 我们包含了第i个元素,并且要立即判断是否满足条件。使用--x, 而不是x--.
            if(--counter[chs[i]] > -1) {
                count--;
            }
            // 因为我们需要的是最值,中间值我们不在乎,所以一次收敛到最小。
            while(count == 0) {     // 快速收紧
                if(i -j + 1< minLen) {
                    minLen = i - j + 1;
                    start = j;
                }
                // 用 == 0 判断是因为有的substring里个别字符可能偏多,多余的字符不会算作满足条件。下方有简单的例子。 
                // 一次只移动一个char, 所以这里用 == 0
                if(counter[chs[j++]]++ == 0) {
                    count++;
                }
            }
        }
        
        return minLen == Integer.MAX_VALUE ? "" : new String(chs, start, minLen);
    }
}

X表示占位符,不是字母X。(使用空格会被自动消除,所以这里用了X)
S: ADOBECODEBANC T: ABC
i: ADOBECXXXXXXXX
j: XDOBECXXXXXXX
i: XDOBECODEBAXX
j: XXXXXXODEBAXX
注意这一步j最后落在了C后面,而不是B的后面。因为B在T里只出现了一次, i在扫描的时候会减小两次,counter[B] = -1, counter[c] = 0
i: XXXXXXODEBANC
j: XXXXXXXXXBANC

将S,T里字符换成String,变成Leetcode 30, Substring with Concatenation of All Words.
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.
s: "barfoothefoobarman", words: ["foo", "bar"], You should return the indices: [0,9]

字符长度为K,我们可能从0, 1, 2, K-1开始组成不同字符, 从K开始会和从O开始重合了。代码如下,不过多解释,可以直接跳过。

public class Solution {
    public List findSubstring(String s, String[] words) {
        List res = new ArrayList();
        int N = s.length(), M = words.length, K = words[0].length();
        // check input
        if(s == null || K == 0 || N < M*K) return res;
        Map map = new HashMap<>(), curMap = new HashMap<>();
        // initial counter
        for(String word: words) {
            map.put(word, map.getOrDefault(word,0) + 1);
        }
        // str denote first word, temp denote last word
        String str = null, temp = null;
        for(int i=0; i< K; i++) { 
            int count = 0;
            // two poniter scan, each time move K steps
            for(int l=i, r=i; r+K <= N; r += K) {
                str = s.substring(r, r+K);
                if(map.containsKey(str)) {
                    curMap.put(str, curMap.getOrDefault(str,0) + 1);
                    
                    if(curMap.get(str) <= map.get(str)) count++;
                    // has a word above the counter of it
                    while(curMap.get(str) > map.get(str)) {
                        temp = s.substring(l, l+K);
                        curMap.put(temp, curMap.get(temp) - 1);
                        l += K;
                        if(curMap.get(temp) < map.get(temp)) count--;
                    }
                    // should be exactly M, when meet, delete last word
                    if(count == M){
                        res.add(l);
                        temp = s.substring(l, l+K);
                        curMap.put(temp, curMap.get(temp) - 1);
                        l += K;
                        count--;
                    }
                } else {
                    curMap.clear();
                    count = 0;
                    l = r + K;
                }
            }
            curMap.clear();
        }

        return res;
    }
}

下面来三个longest substring.
LC3 Longest Substring Without Repeating Characters
需要查重并且记录上次出现的位置,选择map.
以abcdba为例,i走到b, 用map做检查,发现出现过,把j移到map.get(b)的下一个。
i走到a, 用map做检查,发现出现过,把j移到map.get(a)的下一个。发现不对,因为下一个字符是b已经出现过两次,所以需要一个比较,取map.get(b),map.get(a)的最大值。

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        // empty check
        if(s == null || s.length() == 0) return 0;
        // 
        Map map = new HashMap<>();
        // scan whole string and calculte
        int len = 0;
        char[] chs = s.toCharArray();
        for(int i=0, j=0; i len) {
                len = i-j+1;
            }
        }
        return len;
    }
}

LC340. Longest Substring with At Most K Distinct Characters

Given a string, find the length of the longest substring T that contains at most 2 distinct characters.
For example, Given s = “eceba”,
T is "ece" which its length is 3.

counter记录每个字符出现次数。 count表示有多少个不同字符,题目要求最多出现k个。

public class Solution {
    public int lengthOfLongestSubstringKDistinct(String s, int k) {
        // corner case
        if(s== null || s.length() == 0)  return 0;
        // counter, count denote k
        int[] counter = new int[128];
        // l denote tail, r denote head, two pointer scan
        int j = 0, len = 0, count = 0;
        for(int i=0; i k) { 
                while( --counter[s.charAt(j++)] > 0);   
                count--;
            }
            if(i - j + 1 > len) {
                len = i - j +1;
            }
        }
        // return max
        return len;
    }
}

LC159. Longest Substring with At Most Two Distinct Characters

Given a string, find the length of the longest substring T that contains at most 2 distinct characters.
For example, Given s = “eceba”,
T is "ece" which its length is 3.

是上个题目的简易版,或者特殊版。只有两个字符,我们就不需要一个数组了,只需要两个位置标记就行。p1, p2. 为了让计算最值方便,让p1始终小于p2.

这里需要注意的就是p1,p2在repeating characters出现的时候,同时会指向这个字符串的最后一个。
在判断第三个字符是否出现的时候,首先要比较p1 != p2.

public class Solution {
    public int lengthOfLongestSubstringTwoDistinct(String s) {
        if(s.isEmpty()) return 0;
        int p1 = 0, p2 = 0, max = 1, len = 1; 
        char[] chs = s.toCharArray();
        for(int i=1; i max) max = len;
                len = i - p1;       // always keep p1,p2 ordered p1<=p2
                p1 = p2;
                p2 = i;
            } else {  
                if(chs[i] == chs[p1]) {  // 0 or repeating charcters lead to p1=p2
                    p1 = p1 == p2 ? i:p2;
                }
                len++;
                p2 = i;
            }
        }
        
        if (len > max) max = len;
        return max;
    }
}

今天先写到这里,以后补充。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66540.html

相关文章

  • [LintCode/LeetCode] Minimum Window Substring

    Problem Given a string source and a string target, find the minimum window in source which will contain all the characters in target. Notice If there is no such window in source that covers all charac...

    Corwien 评论0 收藏0
  • Leetcode[76] Minimum Window Substring

    LeetCode[76] Minimum Window Substring Given a string S and a string T, find the minimum window in S whichwill contain all the characters in T in complexity O(n). For example, S = ADOBECODEBANC T = AB...

    suemi 评论0 收藏0
  • [Leetcode] Minimum Window Substring 最小字符串窗口

    摘要:双指针法复杂度时间空间思路用一个哈希表记录目标字符串每个字母的个数,一个哈希表记录窗口中每个字母的个数。先找到第一个有效的窗口,用两个指针标出它的上界和下界。 Minimum Window Substring Given a string S and a string T, find the minimum window in S which will contain all the...

    Yuanf 评论0 收藏0
  • leetcode-76-Minimum Window Substring

    摘要:逐步逼近法,类似于牛顿迭代法。重点是找到规律,然后将规律加以表示。动态规划,相邻两个位置之间的关系。字符串的叠加,可以增加共性,通过相减可以得到边界位置处符合规律的要求。学会将问题转化为可求的边界问题。 吃透题目: 任何问题的解决在于理解题目,挖掘本质。 这道题目,从一个string中找包含char的最小子序列,O(n),要求只能遍历一遍。 每次移动一个index,都尝试找子序列,通...

    edagarli 评论0 收藏0
  • [LeetCode] 727. Minimum Window Subsequence

    Problem Given strings S and T, find the minimum (contiguous) substring W of S, so that T is a subsequence of W. If there is no such window in S that covers all characters in T, return the empty string...

    kaka 评论0 收藏0

发表评论

0条评论

Pines_Cheng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<