6/14, Two Pointers, 双指针,窗口类


Minimum Size Subarray Sum

这种看起来有点 greedy 味道的双指针都不同程度上利用后面状态的增长性质,直接排除一些元素,减少搜索范围。

在这道题里,如果 [i - j] 的 subarray 已经 >= target 了,考虑任何 j 以后的元素都是没有意义的,因为数组都是正数,依然会 >= target,长度还一定比当前的长。

当sum大于等于target的时候, 用while loop来增加left指针, 因为有可能rightmost新添加了一个数100, 而left指针连续指了1, 1, 1, 这样的数字, 还有减少length的空间.

public class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int left = 0; int sum = 0;int minLen = Integer.MAX_VALUE;
        for(int i=0;i<nums.length;i++){
            sum += nums[i];
            while(sum>=s){
                minLen = Math.min(i-left+1, minLen);
                sum-=nums[left];
                left++;
            }
        }
        return minLen==Integer.MAX_VALUE?0:minLen;
    }
}

Maximum Size Subarray Sum Equals k

这题的做法其实和上一题没啥关系,也不属于这个分类。。不过看在长得非常像的份上就一起写了吧 = =

这道题允许有负数的存在, 这样就不能用sliding window, sliding window在增加的时候是保证单调性的, 当sum>target的时候可以停止移动right指针.

这题其实是 prefix sum array + two sum,利用前缀和数组实现快速区间和查询,同时 two sum 的方法快速地位 index.

这种 prefix sum 的下标要格外小心,很容易标错。。target value 差也是,写之前多手动过几个 case 保平安。

    public int maxSubArrayLen(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int sum = 0;map.put(0,-1);
        int result = 0;
        for(int i=0;i<nums.length;i++){
            sum += nums[i];
            if(map.containsKey(sum-k)){
                result = Math.max(result, i-map.get(sum-k));
            }
            if(!map.containsKey(sum))
                map.put(sum, i);
        }
        return result;
    }

Longest Substring Without Repeating Characters

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s==null || s.length()==0)  return 0;
       HashMap<Character, Integer> map = new HashMap<Character, Integer>();
       int left = 0;int ans = Integer.MIN_VALUE;int count = 0;
       int i=0;
       for(;i<s.length();i++){
           char ch = s.charAt(i);
           if(!map.containsKey(ch)){
               map.put(ch,1);
           }
           else{
               ans = Math.max(ans, i-left);
               while(map.containsKey(ch)){
                   map.remove(s.charAt(left));
                   left++;
               }
               map.put(ch,1);
           }
       }
       return Math.max(ans, i-left);
    }

}

以下代码值得借鉴的地方, 因为一共char 256个字符, 所以建立boolean[256] 数组够用, left指针i可以通过外层循环loop来递增, 这个trick目前掌握的还不太好, 慎用.

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() <= 1) return s.length();
        int max = 1;
        boolean[] hash = new boolean[256];
        int j = 0;
        for(int i = 0; i < s.length(); i++){
            while(j < s.length()){
                if(!hash[s.charAt(j)]){
                    hash[s.charAt(j++)] = true;
                } else {
                    break;
                }
            }
            max = Math.max(max, j - i);
            hash[s.charAt(i)] = false;
        }

        return max;
    }
}

Minimum Window Substring

  • 对 Target string 做预处理,返回 int[] hash 和需要 match 的字符串数量;

  • count 记录有效字符数, 当有效字符数等于target.length的时候

  • 移动左指针直到不能移动(移动条件: 当前字符不属于target, 或当前字符数量大于target).

  • 计算min length

public class Solution {
    public String minWindow(String s, String t) {
        if(t.length()>s.length()) return "";
        HashMap<Character, Integer> target = new HashMap<Character, Integer>();
        for(int i=0;i<t.length();i++){
            char c = t.charAt(i);
            target.put(c, target.getOrDefault(c,0)+1);
        }
        int count = 0; int left =0 ; int start =0;int end =0; int min = Integer.MAX_VALUE;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        for(int i=0;i<s.length();i++){
            char c = s.charAt(i);
            if(target.containsKey(c)){
                if(!map.containsKey(c) || map.get(c)<target.get(c))
                    count++;
                map.put(c, map.getOrDefault(c,0)+1);
            }

            if(count==t.length()){
                char tmp = s.charAt(left);
                while(!map.containsKey(tmp)||map.get(tmp)>target.get(tmp)){
                    if(map.containsKey(tmp))
                        map.put(tmp, map.get(tmp)-1);
                    left++;
                    tmp = s.charAt(left);
                }
                if(i-left+1<min){
                    min = i-left+1;
                    start = left;
                    end = i;
                }
            }
        }
        return min==Integer.MAX_VALUE? "" : s.substring(start, end+1);
    }
}

6/15, Two Pointers, 双指针,窗口类


  • 这两题有一个 trick 和 Minimum Window Substring 非常像,就是维护一个 "curCount" 代表目前 (i,j) 之间 match 上的数量,而通过 hash[] 的正负充当计数器的作用。

Longest Substring with At Most Two Distinct Characters

Longest Substring with At Most K Distinct Characters

public class Solution {
    public int lengthOfLongestSubstringTwoDistinct(String s) {
        if(s==null || s.length()==0)  return 0;
       HashMap<Character, Integer> map = new HashMap<Character, Integer>();
       int left = 0;int k = 2;int ans = Integer.MIN_VALUE;int count = 0;
       int i=0;
       for(;i<s.length();i++){
           char ch = s.charAt(i);
           if(map.containsKey(ch)){
               map.put(ch, map.get(ch)+1);
           }
           else{
               map.put(ch,1);
               count++;
           }
           if(count>k){
               ans = Math.max(ans, i-left);
               while(count>k){
                   if(map.get(s.charAt(left))==1){
                       map.remove(s.charAt(left));
                       count--;
                   }
                   else
                        map.put(s.charAt(left),map.get(s.charAt(left))-1);
                    left++;
               }
           }
       }
       return Math.max(ans, i-left);
    }
}

Smallest Range

Square Onsite第二轮题,面试官给了一个Google Search Webpage,对关键词搜索的例子。顺着当时的思路撸了一个。先选一组,再move min,试图缩小range,81/86后TLE。面试时Square喜欢当场出正确结果,而对复杂度没那么关心的时候,我选择了简单 快速 直接。面试官问能否优化的时候,我说priority queue,居然被否定了。论坛里最后的高票答案就是pq每次抽取最小min tuple,加入新min tuple,max在每次加入新tuple的时候可以维护。

public int[] smallestRange(int[][] nums) {
        PriorityQueue<Element> pq = new PriorityQueue<Element>(new Comparator<Element>() {
            public int compare(Element a, Element b) {
                return a.val - b.val;
            }
        });
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; i++) {
            Element e = new Element(i, 0, nums[i][0]);
            pq.offer(e);
            max = Math.max(max, nums[i][0]);
        }
        int range = Integer.MAX_VALUE;
        int start = -1, end = -1;
        while (pq.size() == nums.length) {

            Element curr = pq.poll();
            if (max - curr.val < range) {
                range = max - curr.val;
                start = curr.val;
                end = max;
            }
            if (curr.idx + 1 < nums[curr.row].length) {
                curr.idx = curr.idx + 1;
                curr.val = nums[curr.row][curr.idx];
                pq.offer(curr);
                if (curr.val > max) {
                    max = curr.val;
                }
            }
        }

        return new int[] { start, end };
    }

    class Element {
        int val;
        int idx;
        int row;

        public Element(int r, int i, int v) {
            val = v;
            idx = i;
            row = r;
        }
    }
public class Solution {

    class Tuple{
        int idx1;
        int idx2;
        int val;
        public Tuple(int idx1, int idx2, int val){
            this.idx1 = idx1;
            this.idx2 = idx2;
            this.val = val;
        }
    }

    public Tuple getMin(List<Tuple> list){
        int curMin = Integer.MAX_VALUE; Tuple min = null;
        for(Tuple t : list)
            if(t.val<curMin){
                min = t; curMin = t.val;
            }
        return min;
    }

    public Tuple getMax(List<Tuple> list){
        int curMax = Integer.MIN_VALUE; Tuple max = null;
        for(Tuple t : list)
            if(t.val>curMax){
                max = t; curMax = t.val;
            }
        return max;
    }

    public int[] smallestRange(List<List<Integer>> nums) {
        ArrayList<Tuple> list = new ArrayList<Tuple>(); int idx = 0; 
        for(List<Integer> l : nums){
            list.add(new Tuple(idx++, 0, l.get(0)));
        }

        int diff = Integer.MAX_VALUE; int[] ans = new int[2];
        while(true){
            Tuple min = getMin(list);
            Tuple max = getMax(list);
            //System.out.println("min: " + min.val + " max: " + max.val);
            if(max.val-min.val<diff){
                ans[0] = min.val; ans[1] = max.val; diff = max.val-min.val;
            }
            list.remove(min);
            if(min.idx2 == nums.get(min.idx1).size()-1) break;
            list.add(new Tuple(min.idx1, min.idx2+1, nums.get(min.idx1).get(min.idx2+1)));
            min = getMin(list);
        }

        return ans;
    }
}

Max Consecutive Ones II

最简单的方式是存一个prevCount的变量,O(1) space, 每次碰到0的时候,遇到1的数量是prevCount+Count+1,如果0后面跟的是1的话,prevCount=count, 否则prevCount=0.

注意两个corner case,[1,1,1,1] ,一个0都没有出现,ans是count的值。

最后一个是1,循环完以后要补一个判断。不要忽视很多循环都最后要判断一下,

public class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int prevCount = 0; int count = 0; int ans = 0; boolean zero = false;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==1) count++;
            else{
                ans = Math.max(ans, prevCount+count+1);
                if(i+1>=nums.length||nums[i+1]==1) prevCount = count;
                else prevCount = 0;
                count=0;
                zero = true;
            }
        }
        if(zero) ans = Math.max(ans, prevCount+count+1);
        else ans = Math.max(ans, count);
        return ans;
    }
}

简洁解法是two pointers(sliding window)的解法。这个同时可以扩展到k个0的情况。然而这个需要保存部分input,没法拓展到stream的情况

public int findMaxConsecutiveOnes(int[] nums) {
        int max = 0, zero = 0, k = 1; // flip at most k zero
        for (int l = 0, h = 0; h < nums.length; h++) {
            if (nums[h] == 0)                                           
                zero++;
            while (zero > k)
                if (nums[l++] == 0)
                    zero--;                                     
            max = Math.max(max, h - l + 1);
        }                                                               
        return max;             
    }

stream 的情况下,不需要保存input,只需要记录0的index出现的位置。

public int findMaxConsecutiveOnes(int[] nums) {                 
        int max = 0, k = 1; // flip at most k zero
        Queue<Integer> zeroIndex = new LinkedList<>(); 
        for (int l = 0, h = 0; h < nums.length; h++) {
            if (nums[h] == 0)
                zeroIndex.offer(h);
            if (zeroIndex.size() > k)                                   
                l = zeroIndex.poll() + 1;
            max = Math.max(max, h - l + 1);
        }
        return max;                     
    }

results matching ""

    No results matching ""