1009 words
5 minutes
日拱两卒(十)
2026-08-14

10#

和为K的子数组。

枚举左端点,维护潜在目标右端点的数量。

class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
n = len(nums)
s = [0 for _ in range(n + 1)]
cnt = defaultdict(int)
for i in range(n):
s[i + 1] = s[i] + nums[i]
cnt[s[i + 1]] += 1
ans = 0
for l in range(1, n + 1):
ans += cnt[s[l - 1] + k]
cnt[s[l]] -= 1
return ans

9#

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

字母的种类和个数一样,顺序不要求一样就是异位词。维护定长滑动窗口里的字母种类和个数。

class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
n, m = len(s), len(p)
target = defaultdict(int)
for i in range(m):
target[p[i]] += 1
cnt = defaultdict(int)
l = 0
ans = []
def check() -> bool:
for c in cnt:
if target[c] != cnt[c]:
return False
return True
for r in range(n):
cnt[s[r]] += 1
if r - l + 1 > m:
cnt[s[l]] -= 1
l += 1
if r - l + 1 == m and check():
ans.append(l)
return ans

8#

无重复字符的最长子串。

还是滑动窗口。

class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
n = len(s)
cnt = defaultdict(int)
ans = 0
l = 0
for r in range(n):
cnt[s[r]] += 1
for c in cnt:
while cnt[c] > 1:
cnt[s[l]] -= 1
l += 1
ans = max(ans, r - l + 1)
return ans

7#

接雨水。

每一个位置的贡献是min(左侧最高木板,右侧最高木板) - 该位置模板高度。维护前后缀最大值很容易算。

题解的优化是把空间降到O(1),相向双指针,原理是木板短的那一侧都能确定贡献,所以能移动这一侧的指针。不预计算数组而是动态维护两个变量。

没啥必要。

class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
pre_max = [-1 for _ in range(n)]
suf_max = [-1 for _ in range(n)]
pre_max[0] = height[0]
for i in range(1, n):
pre_max[i] = max(pre_max[i - 1], height[i])
suf_max[n - 1] = height[n - 1]
for i in range(n - 2, 0, -1):
suf_max[i] = max(suf_max[i + 1], height[i])
ans = 0
for i in range(1, n - 1):
ans += max(0, min(pre_max[i - 1], suf_max[i + 1]) - height[i])
return ans

6#

三数之和。

固定一位,剩下两位靠排序带来的单调性做双指针。

如果不用set()去重的话要跳过重复元素。

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

5#

盛最多水的容器。

相向双指针,动长的那根木板一定没收益,所以每次动短的那根木板。

class Solution:
def maxArea(self, height: List[int]) -> int:
n = len(height)
l, r = 0, n - 1
ans = (n - 1) * min(height[0], height[n - 1])
while l < r:
if height[l] < height[r]:
l += 1
else:
r -= 1
ans = max(ans, (r - l) * min(height[l], height[r]))
return ans

4#

移动零。

维护不变量,把非零元素都换到前面来。

写成把零元素都换到后面去的逻辑会导致非零元素的原本顺序被打乱。

class Solution:
def moveZeroes(self, nums: List[int]) -> None:
end = 0
n = len(nums)
for i in range(n):
if nums[i] != 0:
nums[end], nums[i] = nums[i], nums[end]
end += 1
return

3#

最长连续序列。

101 5 1 2 3 9 4的最长连续序列指1 2 3 4 5

维护个数,记忆化,每个元素最多被遍历一次。

class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
n = len(nums)
cnt = defaultdict(int)
for i in range(n):
cnt[nums[i]] += 1
@cache
def dfs(u) -> int:
if not cnt.get(u - 1):
return 1
return 1 + dfs(u - 1)
ans = 0
for c in cnt:
ans = max(ans, dfs(c))
return ans

2#

字母异位词分组。

把字符串按照字符种类和个数分类。

class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = []
tot = 0
mp = {}
for s in strs:
key = ''.join(sorted(s))
if key not in mp:
ans.append([s])
mp[key] = tot
tot += 1
else:
ans[mp[key]].append(s)
return ans

1#

两数之和。

注意要求了一个数不能用两次。

终于!结束了!

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
mp = {}
for i in range(n):
if target - nums[i] in mp:
return [i, mp[target - nums[i]]]
mp[nums[i]] = i