笔试面试——滑动窗口

推荐阅读算法思维系列/滑动窗口技巧.

子串问题

LC76. Minimum Window Substring

题目链接:Minimum Window Substring

在这里插入图片描述

from collections import defaultdict

class Solution:
    def minWindow(self, s: str, t: str):
        left, right = 0, 0
        window = defaultdict(int)
        
        # “给出2字符串S和T,求S中包含T的所有字母的某些子串”需要的变量
        needs = defaultdict(int)
        match = 0  # window匹配了多少种needs中的字符
        for c in t:  # 初始化needs
            needs[c] += 1
            
        # 本题需要的记录返回值的变量
        start, min_len = 0, float('inf')
        
        
        while right < len(s):
        
            c1 = s[right]  # 右移right的同时需要维护window和match(5行)
            if c1 in needs:  
                window[c1] += 1
                if window[c1] == needs[c1]:  # 注意这里是=而不是≥,因为只需要在相等时记录该字符的次数已经足够,如果后面还有该字符,也不能加大match,所以对某个字符来说只会加一次match
                    match += 1 
            
            while match == len(needs):  # 每右移一步就检查是否满足题意的充分条件(match==len(needs)说明所有字母都已出现,但是字母出现次数大于等于需要的次数),符合则将left右移多步直到不满足
            
                if right - left + 1 < min_len:  # 检查是否满足题意的必要条件(找到符合条件的最短子串)
                    start = left
                    min_len = right - left + 1
                    
                c2 = s[left]  # 右移left的同时需要维护window和match(5行)
                if c2 in needs:  
                    window[c2] -= 1
                    if window[c2] < needs[c2]:  # 注意这里是<,当≥时都只减去window[c2],不见match,直到减完后小于needs[c2],这时减match,而下轮循环就会跳出(match != len(needs))。所以整个循环只会减一次match
                        match -= 1
                left += 1  # 内层循环的末尾将left右移
            right += 1  # 外层循环的末尾将right右移
            
        return s[start:start + min_len] if min_len < float('inf') else ''

LC438. Find All Anagrams in a String

题目链接:Find All Anagrams in a String

在这里插入图片描述

from collections import defaultdict

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        left,right=0,0
        window=defaultdict(int)
        
        # “给出2字符串S和T,求S中包含T的所有字母的某些子串”需要的变量
        needs=defaultdict(int)
        match=0
        for c in p:  # 初始化needs
            needs[c]+=1
        
        # 本题需要的记录返回值的变量
        res=[]
            
        while right<len(s):
        
            c1=s[right]  # 右移right的同时需要维护window和match(6行,前5行是为了right+=1)
            if c1 in needs:  
                window[c1]+=1
                if window[c1]==needs[c1]:
                    match+=1
            right+=1
            
            while match==len(needs):  # 每右移一步就检查是否满足题意的充分条件(match==len(needs)说明所有字母都已出现,但是字母出现次数大于等于需要的次数),符合则将left右移多步直到不满足
            
                if right-left+1==len(p):  # 检查是否满足题意的必要条件(子串长度恰好等于模板长度),是则更新返回值
                    res.append(left)
                
                c2=s[left]  # 右移left的同时需要维护window和match(6行,前5行是为了left+=1)
                if c2 in needs:  
                    window[c2]-=1
                    if window[c2]<needs[c2]:
                        match-=1
                left+=1 
            
        return res

LC3. Longest Substring Without Repeating Characters

题目链接:Longest Substring Without Repeating Characters

在这里插入图片描述
PS:注意本题的window存放字符次数的含义与上面两题不同,前面两题window存放的是窗口left到right(包含)子串中出现了needs中的字符以及次数,而本题没有needs,window记录的是窗口中所有字符的次数

from collections import defaultdict

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        left,right=0,0
        window=defaultdict(int)
        
        # 本题需要的记录返回值的变量
        res=0
        
        while right<len(s):
        
            c1=s[right]  # 右移right的同时需要维护window(3行,前2行是为了right+=1)
            window[c1]+=1  
            right+=1
            
            while window[c1]>1:  # 每右移一步就检查是否满足题意的“充分”条件(子串含有重复字符),符合则将left右移多步直到不满足
            
                c2=s[left]  # 右移left的同时需要维护window(3行,前2行是为了left+=1)
                window[c2]-=1  
                left+=1
                
            # 只有结束了内层循环才满足必要条件(子串无重复字符),此时检查是否需要更新返回值
            res=max(res,right-left+1)
            
        return res

剑指offer:和为S的连续正数序列

题目链接:https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe

在这里插入图片描述

参考答案:

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # https: // www.nowcoder.com / questionTerminal / c451a3fd84b64cb19485dad758a55ebe?f = discussion
        result = []
        # 两个起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
        low, high = 1, 2
        while low < high:
            # 由于是连续的,差为1的一个序列,那么求和公式是(a0+an) * n / 2
            cur = (high + low) * (high - low + 1) / 2
            # 相等,那么就将窗口范围的所有数添加进结果集
            if cur == tsum:
                result.append([i for i in range(low, high + 1)])
                low += 1
            #  如果当前窗口内的值之和小于sum,那么右边窗口右移一下
            elif cur < tsum:
                high += 1
            else:
                # 如果当前窗口内的值之和大于sum,那么左边窗口右移一下
                low += 1
        return result

其他

剑指offer:递增数组中和为S的两个数字

题目链接:https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b

在这里插入图片描述
解题思路:
在这里插入图片描述

参考答案:

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, arr, tsum):
       # 链接:https://www.nowcoder.com/questionTerminal/390da4f7a00f44bea7c2f3d19491311b?f=discussion
        n = len(arr)
        i, j = 0, n - 1
        while i < j:
            cur = arr[i] + arr[j]
            if cur == tsum:
                return [arr[i],arr[j]]
            elif cur > tsum:
                j-=1
            else:
                i+=1
        return []
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章