LeetCode

3. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

思路:
right指针不断右移,对应的字符个数加1。如果一个字符的个数为两个,则说明出现了重复字符,需要将left指针不断右移,直到和right指针位置重合。

执行用时:3 ms, 在所有 Java 提交中击败了97.09%的用户。

  1. class Solution {
  2. public int lengthOfLongestSubstring(String s) {
  3. int len = s.length();
  4. if (len < 2) return len;
  5. char[] arr = s.toCharArray();
  6. int[] hashmap = new int[128];
  7. int res = 1;
  8. for(int left = 0, right = 0; right < len; right++) {
  9. hashmap[arr[right]]++;
  10. if (hashmap[arr[right]] == 2) {
  11. while (hashmap[arr[right]] == 2) {
  12. hashmap[arr[left]]--;
  13. left++;
  14. }
  15. }
  16. res = Math.max(res, right - left + 1);
  17. }
  18. return res;
  19. }
  20. }

438. 找到字符串中所有字母异位词

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

思路:
s中的字符存在于p中,通过hash中对应字母的值是否大于零来判断。如果存在则将hash中该字符的个数减1,right右边界加1,count计数加1。
确定了第一段符合的字符串后,left右移,恢复hash中字符的值和count的值。从第一段符合的字符串后开始重新走循环。当走到不存在p中的字符时,走else,给字符在hash中的个数加1,count减1。然后在走if时会将该字符在hash中的个数减1,count加1。不存在的字符不会影响hash中字符个数和count的计算。

执行用时:4 ms, 在所有 Java 提交中击败了99.97%的用户。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int slen = s.length(), plen = p.length();
        List<Integer> list = new ArrayList<>();

        char[] sarr = s.toCharArray();
        char[] parr = p.toCharArray();
        int[] hashmap = new int[128];
        for (int i = 0; i < parr.length; i++) {
            hashmap[parr[i]]++;
        }

        int left = 0, right = 0, count = 0;
        while (right < slen) {
            if (hashmap[sarr[right]] > 0) {
                hashmap[sarr[right]]--;
                count++;
                right++;
            } else {
                hashmap[sarr[left]]++;
                count--;
                left++;
            }

            if (count == plen) {
                list.add(left);
            }
        }

        return list;
    }
}

76. 最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。

思路:
i作为右指针,left作为左指针。遍历字符串s,对于每一个字符,将hash中该字符的个数减1,减1后如果≥0,则count加1。
当count和字符串t的长度相等时,开始循环,记录最小覆盖子串的长度。将left指向的字符对应hash中的值加1,加1后如果>0,则说明该字符存在于字符串t中,此时当前子串和t不匹配,需要将count减1,跳出循环。

执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户。

class Solution {
    public String minWindow(String s, String t) {
        int sLen = s.length(), tLen = t.length();
        int[] hashmap = new int[128];
        char[] sarr = s.toCharArray();
        char[] tarr = t.toCharArray();
        for (int i = 0; i < tLen; i++) {
            hashmap[tarr[i]]++;
        }
        int min = sLen + 1, count = 0, left = 0, minLeft = 0;

        for (int i = 0; i < sLen; i++) {
            if (--hashmap[sarr[i]] >= 0) {
                count++;
            }
            while (count == tLen) {
                if (min > i - left + 1) {
                    min = i - left + 1;
                    minLeft = left;
                }
                if (++hashmap[sarr[left]] > 0) {
                    count--;
                }
                left++;
            }
        }

        if (min < sLen + 1) {
            return s.substring(minLeft, minLeft + min);
        } else {
            return "";
        }
    }
}

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

思路:
当累加和sum满足≥s时,记录窗口长度,并开始移动左窗口,直到不满足条件。

执行用时:1 ms, 在所有 Java 提交中击败了99.87%的用户。

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int len = nums.length;
        int res = len + 1, sum = 0, left = 0;

        for (int i = 0; i < len; i++) {
            sum += nums[i];
            while (sum >= s) {
                if (res > i - left + 1) {
                    res = i - left + 1;
                }
                sum -= nums[left];
                left++;
            }
        }

        if (res < len + 1) {
            return res;
        } else {
            return 0;
        }
    }
}

练习:

567. 字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        char[] arr1 = s1.toCharArray();
        char[] arr2 = s2.toCharArray();
        int[] hashmap = new int[128];
        int len = arr1.length;
        for (int i = 0; i < len; i++) {
            hashmap[arr1[i]]++;
        }

        boolean flag = false;
        int left = 0, right = 0, count = 0;
        while (right < arr2.length) {
            if (hashmap[arr2[right]] > 0) {
                hashmap[arr2[right]]--;
                count++;
                right++;
            } else {
                hashmap[arr2[left]]++;
                count--;
                left++;
            }
            if (count == len) {
                return true;
            }
        }
        return false;
    }
}

剑指 Offer

剑指 Offer 48. 最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

思路:
双指针。重点:while (hashmap[arr[right]] == 2)
执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:40.1 MB, 在所有 Java 提交中击败了100.00%的用户

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        if (len < 2) return len;

        char[] arr = s.toCharArray();
        int[] hashmap = new int[128];

        int res = 1;
        for(int left = 0, right = 0; right < len; right++) {
            hashmap[arr[right]]++;

            if (hashmap[arr[right]] == 2) {
                while (hashmap[arr[right]] == 2) {
                    hashmap[arr[left]]--;
                    left++;
                }
            }

            res = Math.max(res, right - left + 1);
        }

        return res;
    }
}

剑指 Offer 59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

思路:
单调队列。维持一个单调递减的队列。如果队列中索引的差值大于等于窗口的大小,则弹出队首。如果当前元素比度尾元素要大,则一直弹出队尾元素,知道当前元素小于队尾元素或队列为空。然后将当前元素添加到队尾。如果当前索引达到了窗口的大小,则存储窗口最大值。
执行用时:15 ms, 在所有 Java 提交中击败了53.09%的用户
内存消耗:48 MB, 在所有 Java 提交中击败了100.00%的用户

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[]{};
        }

        int[] res = new int[nums.length - k + 1];
        Deque<Integer> queue = new ArrayDeque<>();
        for (int i = 0, j = 0; i < nums.length; i++) {
            if (!queue.isEmpty() && i - queue.peek() >= k) {
                queue.poll();
            }
            while (!queue.isEmpty() &&  nums[i] > nums[queue.peekLast()]) {
                queue.pollLast();
            }
            queue.offer(i);
            if (i >= k - 1) {
                res[j++] = nums[queue.peek()];
            }
        }

        return res;
    }
}

剑指 Offer 59 - II. 队列的最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

思路:
本质上是一个求滑动窗口最大值的问题,套单调队列模板。这个队列可以看成是一个滑动窗口,入队就是将窗口的右边界右移,出队就是将窗口的左边界右移。

class MaxQueue {
    private Deque<Integer> queue;
    private Deque<Integer> help;

    public MaxQueue() {
        queue = new ArrayDeque<>();
        help = new ArrayDeque<>();
    }

    public int max_value() {
        return queue.isEmpty() ? -1 : help.peek();
    }

    public void push_back(int value) {
        queue.offer(value);
        while(!help.isEmpty() && value > help.peekLast()) {
            help.pollLast();
        }
        help.offer(value);
    }

    public int pop_front() {
        if(queue.isEmpty()) {
            return -1;
        }
        int val = queue.pop();
        if(help.peek() == val) {
            help.pop();
        }
        return val;
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */