Leetcode刷题(9) 双指针之滑动窗口系列(多用于字符串匹配)

Leetcode刷题(9) 双指针之滑动窗口系列(多用于字符串匹配)

76. 最小覆盖子串

方法: 滑动窗口, 双指针

class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        # 维护两个字典,其中都记录着字符和对应的频数
        windows = dict()  # 窗口内的need中包含的字符及其频数
        need = dict()     # 目标字符及其频数
        for n in t:
            need[n] = need.get(n, 0) + 1
        
        valid = 0  # 已经ok的字符(窗口中该字符的次数>=need中对应字符的次数)

        # 双指针(搜索区间左闭右开)
        left = 0
        right = 0
        # 初始化最短长度
        lenght = float('Inf')
        nums = len(s)

        # 记录最短的子串的开始和结束位置
        start = 0
        end = nums

        # 外层right的循环; 内层left的循环
        while(right < nums):

            cur = s[right]
            right += 1
            if cur in need.keys():  # 加入的cur在need中
                windows[cur] = windows.get(cur, 0) + 1
                if windows[cur] == need[cur]:
                    valid += 1
            
            while(valid == len(need)): # 满足→条件
                # 先处理完任务之后, 再移动left
                if lenght > float(right - left):
                    lenght = right - left
                    start = left
                    end = right

                d = s[left]
                left += 1
                if d in need.keys():  # 删除的d在need中
                    if windows[d] == need[d]:
                        valid -= 1
                    windows[d] = windows.get(d, 0) - 1

        return s[start: end] if lenght!=float('Inf') else ""


                    

567. 字符串的排列

class Solution(object):
    def checkInclusion(self, s1, s2):
        """
        :type s1: str
        :type s2: str
        :rtype: bool
        """
        need = dict()
        for in_s1 in s1:
            need[in_s1] = need.get(in_s1, 0) + 1
        windows = dict()

        valid = 0

        s1_lenght = len(s1)
        s2_lenght = len(s2)
        
        left = 0
        right = 0

        while(right < s2_lenght):
            r = s2[right]
            right += 1
            if r in need:
                windows[r] = windows.get(r, 0) + 1
                if windows[r] == need[r]:
                    valid += 1
                    
            # 排列的长度一定是等于p的长度,所以当windows的长度等于p_size就可以判断结果和开始收缩了
            while(right - left >= s1_lenght):
                if valid == len(need):
                    return True

                l = s2[left]
                left += 1
                if l in need:
                    if windows[l] == need[l]:
                        valid -= 1
                windows[l] = windows.get(l, 0) - 1 
        return False
class Solution(object):
    def checkInclusion(self, s1, s2):
        """
        :type s1: str
        :type s2: str
        :rtype: bool
        """
        need = dict()
        window = dict()
        for c in s1:
            need[c] = need.get(c, 0) + 1

        left = 0
        right = 0
        vaild = 0

        n = len(s2)

        while(right < n):
            r = s2[right]
            right += 1

            if r in need.keys():
                window[r] = window.get(r, 0) + 1
                if window[r] == need[r]:
                    vaild += 1
            # 如果这里使用的是>而不是>=的话,结果判断需要放在当前循环结束后
            while (right - left > len(s1)):
                l = s2[left]
                left += 1
                if l in need.keys():
                    if need[l] == window[l]:
                        vaild -= 1
                    window[l] = window.get(l, 0) - 1
            # 结果判断在这
            if vaild == len(need):
                    return True
        return False





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

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        windows = dict()
        need = dict()
        for n in p:
            need[n] = need.get(n , 0) + 1

        s_size = len(s)
        p_size = len(p)
        
        right = 0
        left = 0

        valid = 0
        target_valid = len(need.keys())
        
        res = list()

        while(right < s_size):
            # →移动right
            r  = s[right]
            right += 1
        

            # 判断r是否在need中, 并进行后续处理
            if r in need.keys():
                windows[r] = windows.get(r, 0) + 1
                if windows[r] == need[r]:
                    valid += 1
            
            # 排列的长度一定是等于p的长度,所以当windows的长度等于p_size就可以判断结果和开始收缩了
            while(right-left >= p_size):
                # 先判断结果
            
                if valid == target_valid:
                    res.append(left)
                
                # 再→移动left
                l = s[left]
                left += 1
                # 判断l是否在need中, 并进行后续处理
                if l in need.keys():
                    if windows[l] == need[l]:
                        valid -= 1
                    windows[l] = windows.get(l, 0) - 1

        return res

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

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        right = 0
        left = 0
        res = 0
        windows = dict()

        while(right < len(s)):

            r = s[right]
            right += 1
            windows[r] = windows.get(r, 0) + 1

            while(windows[r] > 1):
                l = s[left]
                left += 1

                windows[l] = windows.get(l, 0) - 1
            # 结果的判断更新是在更新完left窗口之后,因为更新完窗口里面的字符才没有重复
            res = max(right - left, res)

        return res


下面是字符串的匹配问题, 我看和上面的也比较相似,所以也放这里来了

28. 实现 strStr()

暴力法:

# 暴力法
class Solution:
    def strStr(self, haystack, needle):
        L = len(needle)
        n = len(haystack)
        # 一定要保证从start开始后面有L个数(包括start上的元素)
        for start in range(n - L + 1):
            # 截取这段和needle进行比较
            if haystack[start: start + L] == needle:
                return start
        return -1

KMP状态机法:

class Solution(object):
    def strStr(self, haystack, needle):
        """
        :type haystack: str
        :type needle: str
        :rtype: int
        """
        M = len(needle)
        n = len(haystack)
        if M == 0:
            return 0
        dp = [[0] * 256 for _ in range(M)]
        # base case
        dp[0][ord(needle[0])] = 1
        def kmp():
            # 影子状态初始化为0
            X = 0
            for j in range(1, M):
                for c in range(256):
                    dp[j][c] = dp[X][c]
                # 下标为j的字符被匹配了, 状态更新为j + 1
                dp[j][ord(needle[j])] = j + 1  
                # 更新影子X的值
                X = dp[X][ord(needle[j])]
        def search():
            j = 0
            for i in range(n):
                j = dp[j][ord(haystack[i])]
                if j == M:
                    return i - M + 1
            return -1
        kmp()
        return search()      

10. 正则表达式匹配

递归法

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        if len(p) == 0:
            return len(s) == 0
        else:
            # 不管其他情况,总是要先计算第一个字符的匹配情况
            # 然后依据第一个字符的匹配情况来对接下来的情况进行处理
            first = len(s) > 0 and p[0] in {'.', s[0]}
            # 遇到通配符
            if len(p) > 1 and p[1] == '*':
                # 匹配0个 or 匹配1个
                return self.isMatch(s, p[2:]) or first and self.isMatch(s[1:], p)
            else:
                # 正常匹配
                return first and self.isMatch(s[1:], p[1:])

带备忘录的递归法

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        ns = len(s)
        np = len(p)
        mome = dict()
        def dp(i, j):
            if (i, j) in mome:
                return mome[(i, j)]
            # p没了
            if j == np:
                # s也应该没了
                return i == ns
            else:
                # 不管其他情况, 总是要先计算第一个字符的匹配情况
                # 然后依据第一个字符的匹配情况来对接下来的情况进行处理

                # 首先保障s中还有未被匹配的字符
                first = i < ns and p[j] in {'.', s[i]}
                # 遇到通配符
                if j < np - 1 and p[j + 1] == '*':
                    # 匹配0个 or 匹配1个
                    ans = dp(i, j + 2) or first and dp(i + 1, j)
                
                # 正常匹配
                else:
                    ans = first and dp(i + 1, j + 1)
                mome[(i, j)] = ans
                return ans
        return dp(0, 0)

424. 替换后的最长重复字符

该种题型没有窗口收缩的操作,只有窗口的平移

class Solution(object):
    def characterReplacement(self, s, k):
        n = len(s)
        right = 0
        left = 0
        maxlenght = 0
        # 记录窗口中出现最大次数的字符的次数
        maxCount = 0
        # 记录了窗口中各种字符的频数
        window = dict()
        while right < n:
            r = s[right]
            window[r] = window.get(r, 0) + 1
            # 更新当前窗口中有最大频数的字符
            maxCount = max(maxCount, window[r])
            # 右扩张
            right += 1
            
            # 更新结果
            if maxCount + k >= right - left:
                maxlenght = max(maxlenght, right - left)
            
            # 左平移
            else:
                l = s[left]
                window[l] -= 1
                left += 1
    
        return maxlenght
                
         

1004. 最大连续1的个数 III

相比上一题就是只增加了一个maxCount的更新条件,只在遇到1的时候更新

class Solution(object):
    def longestOnes(self, A, K):
        n = len(A)
        right = 0
        left = 0
        maxlenght = 0
        # 记录窗口中出现最大次数的字符的次数
        maxCount = 0
        # 记录了窗口中各种字符的频数
        window = dict()
        while right < n:
            r = A[right]
            window[r] = window.get(r, 0) + 1
            # 更新当前窗口中有最大频数的字符
            if r == 1:  # 仅仅加了一个这个条件, 只在遇到1的情况下才更新maxCount
                maxCount = max(maxCount, window[r])
            # 右扩张
            right += 1
            
            # 更新结果, 然后不左缩,而是继续右扩寻找更优解
            if maxCount + K >= right - left:
                maxlenght = max(maxlenght, right - left)
            
            # 左平移
            else:
                l = A[left]
                window[l] -= 1
                left += 1
    
        return maxlenght

1234. 替换子串得到平衡字符串

解法是标准的滑动窗口,但是就是窗口的收缩条件比较难想

收缩条件是:两个指针间是待替换的子串,那么我们只需要保证在两指针区域外的字符的频率都小于n/4

class Solution(object):
    def balancedString(self, s):
        n = len(s)
        minreplace = n
        # count用来记录窗口外['Q', 'W', 'E', 'R']每个字符出现的频度
        count = dict()
        for c in ['Q', 'W', 'E', 'R']:
            count[c] = 0
        for c in s:
            count[c] += 1
        balance = n // 4
        l = 0
        r = 0
        while r < n:
            # 右扩来搜索满足条件的结果
            right = s[r]
            count[right] -= 1
            r += 1
            # 满足条件的时候左缩窗口取得最优解
            while l < n and all(count[c] <= balance for c in ['Q', 'W', 'E', 'R']):
                minreplace = min(minreplace, r - l)
                left = s[l]
                l += 1
                count[left] += 1
        return minreplace

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值