赞
踩
public int problemName(String s) { //Step 1:定义需要维护的变量们(对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表) int x = ……; int y = ……; //Step 2:定义窗口的首尾端(start, end),然后滑动窗口 int start = 0; int len = s.length(); for (int end = 0; end < len; end++) { //Step 3:更新需要维护的变量, 有的变量需要一个if语句来维护(比如最大最小长度) x = new_x; if (condition) { y = new_y; } //------------下面是两种情况,读者请根据题意二选1------------// //Step 4 - 情况1 //如果题目的窗口长度固定:用一个if语句判断一下当前窗口长度是否超过限定长度 //如果超过了,窗口左指针前移一个单位保证窗口长度固定, 在那之前, 先更新Step 1 定义的(部分或所有) 维护变量 if (窗口长度大于限定值) { //更新(部分或所有) 维护变量 //窗口左指针前移一个单位保证窗口长度固定 } //Step 4 - 情况2 //如果题目的窗口长度可变:这个时候一般涉及到窗口是否合法的问题 //如果当前窗口不合法时, 用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法 //在左指针移动之前更新Step 1 定义的(部分或所有) 维护变量 while (不合法) { //更新(部分或所有) 维护变量 //不断移动窗口左指针直到窗口再次合法 } } //Step 5:返回答案 return ...; }
给定一个字符串 s ,找出 至多 包含两个不同字符的最长子串 t ,并返回该子串的长度。
示例 1:
输入: “eceba”
输出: 3
解释: t 是 “ece”,长度为3。
示例 2:
输入: “ccaabbb”
输出: 5
解释: t 是 “aabbb”,长度为5。
思路:
实际上这道题也是双指针的思想。
代码实现:
class Solution { public int lengthOfLongestSubstringTwoDistinct(String s) { int len = s.length(); if (len < 3) { return len; } int max_len = 0; Map<Character, Integer> map = new HashMap<>();//map用来记录子字符串的情况 char[] ch = s.toCharArray(); int start = 0; for (int end = 0; end < len; end++) { char tail = ch[end]; if (map.containsKey(tail)) { map.put(tail, map.get(tail) + 1);//记录该字符的最大长度 } else { map.put(tail, 1);//初次见面 } if (map.size() <= 2) {//更新max_len max_len = Math.max(max_len, end - start + 1); } while (map.size() > 2) {//更新start char head = ch[start]; if (map.containsKey(head)) { map.put(head, map.get(head) - 1); } if (map.get(head) == 0) { map.remove(head); } start++;//往后移动 } } return max_len; } }
给定一个字符串 s ,找出 至多 包含 k 个不同字符的最长子串 T。
示例 1:
输入: s = “eceba”, k = 2
输出: 3
解释: 则 T 为 “ece”,所以长度为 3。
示例 2:
输入: s = “aa”, k = 1
输出: 2
解释: 则 T 为 “aa”,所以长度为 2。
思路:
跟上面那道题一样的,就是把2变成k
实现代码:
class Solution { public int lengthOfLongestSubstringKDistinct(String s, int k) { int len = s.length(); if (len < k+1) { return len; } int max_len = 0; Map<Character, Integer> map = new HashMap<>();//map用来记录子字符串的情况 char[] ch = s.toCharArray(); int start = 0; for (int end = 0; end < len; end++) { char tail = ch[end]; if (map.containsKey(tail)) { map.put(tail, map.get(tail) + 1);//记录该字符的最大长度 } else { map.put(tail, 1);//初次见面 } if (map.size() <= k) {//更新max_len max_len = Math.max(max_len, end - start + 1); } while (map.size() > k) {//更新start char head = ch[start]; if (map.containsKey(head)) { map.put(head, map.get(head) - 1); } if (map.get(head) == 0) { map.remove(head); } start++;//往后移动 } } return max_len; } }
给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
思路:
其实我个人觉得,滑动窗口其实就是一种双指针,一个指左,一个指右。某个条件下左指针移动,某个条件右指针移动。然后用一个int res,动态的记录最值。
实现代码
public static int longestOnes(int[] nums, int k) { int len = nums.length; int res = 0; int i = 0, j = 0; while (i < len && j < len) { if (nums[j] != 0 || k != 0) { if (nums[j++] == 0) { k--; } } else { if (nums[i++] == 0) { k++; } } res = res > j - i ? res : j - i; } return res; }
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
思路:
维护一个长度为K的单调列,然后不停得往后遍历,拿出一个同时将不在视野内的都删除
代码实现:
public int[] maxSlidingWindow(int[] nums, int k) { int n = nums.length; PriorityQueue<int[]> pq = new PriorityQueue<int[]>((pair1, pair2) -> pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1]); for (int i = 0; i < k; ++i) { pq.offer(new int[]{nums[i], i});//前k个 } //排好序了 int[] ans = new int[n - k + 1]; ans[0] = pq.peek()[0]; for (int i = k; i < n; ++i) { pq.offer(new int[]{nums[i], i});//加入一个 while (pq.peek()[1] <= i - k) {//将所有的不在视野内的东西都删除 pq.poll(); } ans[i - k + 1] = pq.peek()[0]; } return ans; }
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
示例 2:
输入:s = “a”, t = “a”
输出:“a”
示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
思路:
很简单,也是双指针的思路。一个快指针在后面遍历,一个慢指针在前面用来缩减字符串。具体思路如下
但是你会发现如此方法能做出来,但是时间复杂度很高。
仔细阅读题目可以发现,字符串都是大小写的英文字母,因此可以采用对字符串的统计数组来简化计算。(实际上思想还是上面的思想)
代码实现
public static String minWindow(String s, String t) { char[] s1 = s.toCharArray();//将s字符串转化为字符数组 char[] t1 = t.toCharArray();//将t字符串转化为字符数组 int n = s1.length;//n记录s字符串的长度 int m = t1.length;//m记录t字符串的长度 int[] arr = new int[58];//z:122 A:65 122-65+1=58//对字符的个数统计使用的数组,如:0就是A即'A'-'A' int fast = 0, slow = 0, count = 0;//定义快指针fast,慢指针slow,记录符合的字符个数count String res = "";//定义返回的结果字符串 //---------先遍历t字符串-----------// for (int i = 0; i < m; i++) {//首先将t中的字符,全部减一 arr[t1[i] - 'A']--; } //---------再遍历s字符串---------// for (fast = 0; fast < n; fast++) {//滑动窗口快指针遍历 arr[s1[fast] - 'A']++;//将快指针遍历过的字符全部加1,表示经过了 if (arr[s1[fast] - 'A'] <= 0) {//如果是目标字符,则前面已经减一过了,所以只会<=0 count++;//记录符合目标字符的个数 } //发现符合情况的字符串,此时我需要将字符串缩减到最短以满足要求 if (count == m) {//如果目标字符的个数等于t字符串的长度就开始慢指针遍历 while (arr[s1[slow] - 'A'] > 0) {//如果慢指针指向的数>0则不是目标字符,就向前移动 arr[s1[slow] - 'A']--;//因为已经脱离了快慢指针的范围就不统计个数了,于是就对1该字符个数减一 slow++;//慢指针移动 } if ("" == res || res.length() > fast - slow + 1) {//因为满足目标字符的条件,就开始截取字符串了 res = s.substring(slow, fast + 1);//截取函数左闭右开,所以要fast+1 } } } return res;//返回对应截取最短的字符串! }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。