我们一开始要找的是 1 开头的序列,只要窗口的和小于 target,窗口的右边界会一直向右移动。假设 \(1+2+\dots+8\) 小于 target,再加上一个 9 之后, 发现 \(1+2+\dots+8+9\) 又大于 target 了。这说明 1 开头的序列找不到解。此时滑动窗口的最右元素是 9。
接下来,我们需要找 2 开头的序列,我们发现,\(2 + \dots + 8 < 1 + 2 + \dots + 8 < \mathrm{target}\)。这说明 2 开头的序列至少要加到 9。那么,我们只需要把原先 1~9 的滑动窗口的左边界向右移动,变成 2~9 的滑动窗口,然后继续寻找。而右边界完全不需要向左移动。
以此类推,滑动窗口的左右边界都不需要向左移动,所以这道题用滑动窗口一定可以得到所有的解。时间复杂度是 \(O(n)\)。
注:这道题当前可以用等差数列的求和公式来计算滑动窗口的和。不过我这里没有使用求和公式,是为了展示更通用的解题思路。实际上,把题目中的正整数序列换成任意的递增整数序列,这个方法都可以解。
作者:nettee
链接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本题题解:
class Solution { public int[][] findContinuousSequence(int target) { // 这得从1开始看起 // 如果 滑动窗口的数字和sum == target 那么就可以加入列表,算一个 // 如果大于,那就前进 left, // 如果小于,前进 right int left = 1; int right = 1; int sum = 0; List<int[]> list = new ArrayList<>(); // while (right <= target) { while (left <= target / 2) { if (sum == target) { int[] arr = new int[right - left]; for (int i = left; i < right; i++) { arr[i - left] = i; } list.add(arr); // 通过了就左边界前进,继续遍历 sum -= left; left++; } else if (sum < target) { sum += right; right++; } else if (sum > target) { sum -= left; left++; } } // return list.toArray(); // 这样是不行的,不然返回的是Object[]数组,得指明数组类型 return list.toArray(new int[list.size()][]); } } 3. 无重复字符的最长子串在上一题中,我们使用双端队列完成了滑动窗口的一道颇为困难的题目,以此展示了什么是滑动窗口。在本节中我们将继续深入分析,探索滑动窗口题型一些具有模式性的解法。
第3题:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
答案直接分析题目,假设我们的输入为“abcabcbb”,我们只需要维护一个窗口在输入字符串中进行移动。如下图:
当下一个元素在窗口没有出现过时,我们扩大窗口。
当下一个元素在窗口中出现过时,我们缩小窗口,将出现过的元素以及其左边的元素统统移出:
在整个过程中,我们记录下窗口出现过的最大值即可。而我们唯一要做的,只需要尽可能扩大窗口。
那我们代码中通过什么来维护这样的一个窗口呢?anyway~ 不管是队列,双指针,甚至通过map来做,都可以。
我们演示一个双指针的做法:
//java public class Solution { public static int lengthOfLongestSubstring(String s) { int n = s.length(); Set<Character> set = new HashSet<>(); int result = 0, left = 0, right = 0; while (left < n && right < n) { //charAt:返回指定位置处的字符 if (!set.contains(s.charAt(right))) { set.add(s.charAt(right)); right++; result = Math.max(result, right - left); } else { set.remove(s.charAt(left)); left++; } } return result; } }