问了ai之后,明白自己应该先梳理思路,在说一些细节问题。先把思路理清,把框架搭建起来。最好可以先写注释,30min之后,这道题不管什么地步,都要暂停去看答案。

11.盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

思路:直觉是从底部最长的开始寻找,两边指针,谁小移动谁,不断更新ans

class Solution:
    def maxArea(self, height: List[int]) -> int:
        res = 0
        i = 0
        j = len(height) - 1
        while i < j:
            h = min(height[i],height[j])
            res = max(res,h*(j-i))
            if height[i] < height[j]:
                i += 1
            else:
                j -= 1
        return res
        

15.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

思路:人类直觉,首先进行排序,遍历数组,然后左右指针。当sum < 0,那就是右移左指针,当sum > 0,那就是左移右指针。当 sum = 0 那就需要把当前 i l r代表的数值维护到结果中,移动双指针,并且跳过l r重复的数值。后续 i 有重复的,需要跳过

class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        nums = sorted(nums)
        res = []
        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            if nums[i] > 0:
                break
            l = i + 1
            r = len(nums) - 1
            
            while l < r:
                if nums[i] + nums[l] + nums[r] == 0:
                    res.append([nums[i],nums[l],nums[r]])
                    while (l < r and nums[l] == nums[l + 1]):
                        l += 1
                    while (l < r and nums[r] == nums[r - 1]):
                        r -= 1
                    l += 1
                    r -= 1
                elif nums[i] + nums[l] + nums[r] < 0:
                    l += 1
                else:
                    r -= 1
            
        return res




        

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

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

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。

思路:需要 dict 维护当前窗口字符对应的索引。遍历字符串,当出现重复字符的时候,需要判断,该字符的索引和 l 的关系,如果 索引 < l,则无影响,需要更新字符对应的索引;如果索引 >= l,则需要更新 l 的位置。 遍历过程需要不断维护最长长度。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 字符串遍历,找到重复的就从重复出现的第一个位置再开始找
        # 维护一个字典,将字符映射到索引
        # 边遍历边维护,当遍历字符是前面出现过的,更新左边界,更新当前字符的字典映射。可是左边界更新之后,中间的映射怎么办?
        dct = {}
        l = 0
        ans = 0
        for i in range(len(s)):
            if s[i] in dct:  # 出现重复
                if dct[s[i]] >= l:
                    l = dct[s[i]] + 1
            dct[s[i]] = i
            ans = max(ans,i - l + 1)
        return ans


        

560.和为K的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

思路:感觉很多时候,计算机中是看右侧的那种感觉。这个是用前缀和pre_sum维护每个位置前面累积和。然后pre_sum之间各个元素的差值,如果是 k 那就是找到了和为k的子数组。这个地方可以用两数之和的思想。 a + b = k 这个题就是  a + (-b) = k。 需要注意的是前缀和需要单独维护pre_sum[0] = 0,然后pre_sum[1]就是第一个元素,pre_sum[2]是第1个元素+第2个元素。因为要考虑到从1 到 i 元素和为 k 。

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        # 前缀和 pre_sum[i] 表示i位置前缀和
        # 前缀和pre_sum 中元素两数之差为k 也就是a+(-b) =k 参考两数之和
        # 构造mp字典,pre_sum元素是key ,value表示当前出现的次数,res维护最后结果
        pre_sum = [0] * (len(nums) + 1)
        # pre_sum[0] = nums[0]
        res = 0
        pre_sum[0] = 0
        for i in range(0,len(nums)):
            
            pre_sum[i + 1] = pre_sum[i] + nums[i]
        mp =  defaultdict(int)
        for i in range(len(pre_sum)):
            if (-k + pre_sum[i]) in mp:
                res += mp[(-k + pre_sum[i])]
            
            mp[pre_sum[i]] += 1
        print(pre_sum)
        return res
        

239.滑动窗口最大值

给你一个整数数组 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

思路:这种题就是人类直觉没有办法模拟的感觉。我觉得可能我之后还是会忘记。现在的思路是维护一个双端队列,表示候选最大值的索引。其中对头为当前窗口的最大值的索引。

遍历数组,如果当前值 > 队尾,那就需要把队尾踢出去;如果 < 队尾,那就当作候选最大值,加入队尾;每次遍历需要看对头的索引和 l 边界的问题,如果已经不在窗口中,需要把对头踢出去。

from collections import deque
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 需要首先找到第一个窗口中的最大值
        # 移动窗口,先判断右面的新进来和当前最大值关系,需不需要更新最大值;以及最大值是否已经出窗口了
        #需要维护最大值对应的索引mp[max_num] >= l and >nums[right] ans = max_num
        # mp[max_num] >=l and < nums[right] ans = nums[right]
        # mp[max_num] <l  重新维护最大值。怎么重新维护,也就是一开始怎么维护窗口中的最大值??用栈??
        # 用双端队列维护一些候选最大值的索引
        # 队头表示当前窗口的最大值,后面的是候选最大值。
        # 窗口移动,需要判断:
        # 右侧新值 >= 队尾值 ,队尾出队,循环
        # 右侧新值 < 队尾值,进队列当候选最大值
        # 移动窗口的左侧边界大于对头索引的话,对头出队
        q = deque()
        r = 0
        ans = -inf
        res = []
        for r in range(len(nums)):
            l = r - k + 1
            if q and q[0] < l:
                q.popleft()
            while q and nums[r] >= nums[q[-1]]:
                q.pop()
            
            q.append(r)
            # ans = max(ans,nums[q[0]])
            if l >= 0:
                res.append(nums[q[0]])
        return res
        
            
            

        

76.最小覆盖子串

给定两个字符串 s 和 t,长度分别是 m 和 n,返回 s 中的 最短窗口 子串,使得该子串包含 t 中的每一个字符(包括重复字符)。如果没有这样的子串,返回空字符串 ""

测试用例保证答案唯一。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

思路:这个也是需要先看右侧的指针,也就是右侧一直找,直到s子串能覆盖t子串了,那就开始缩小左侧指针,不断维护 左指针和右指针。更新s_cnt。

class Solution:
    def check(self,s_cnt,t_cnt):
        for key in t_cnt:
            if s_cnt[key] < t_cnt[key]:
                return 0
        return 1
    def minWindow(self, s: str, t: str) -> str:
        # 1、准备s_cnt 和 t_cnt分别统计字符串中字符出现的数量
        # 右指针是探索者,一直右移直到s_cnt覆盖了t_cnt
        # 左指针是清理者,当覆盖了之后,左指针右移,s_cnt[s[l]]-- ,l++ 直到不能覆盖为止 更新l 和r 直到最后把l r 之间的字符串打印
        s_cnt = defaultdict(int)
        t_cnt = defaultdict(int)
        for i in range(len(t)):
            t_cnt[t[i]] += 1
        print(t_cnt)
        l = 0
        r = 0
        ans = inf
        r_min = inf
        l_min = inf
        for r in range(len(s)):
            s_cnt[s[r]] += 1
            while self.check(s_cnt,t_cnt):
                if ans > (r - l + 1):
                    r_min = r
                    l_min = l
                    ans = r - l + 1
                s_cnt[s[l]] -= 1
                l += 1
        print(s_cnt)
        print(r_min,l_min)
        if ans == inf:
            return ""
        else: 
            return s[l_min:r_min + 1]

            


                

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐