class Solution(object):
def longestValidParentheses(self, s):
dp = [0 for x in range(len(s))]
max_to_now = 0
for i in range(1,len(s)):
if s[i] == ')':
# case 1: ()()
if s[i-1] == '(':
# 在字符创末尾遇到左括号"("是构不成有效的括号的,所以就等于最近的括号加上2
dp[i] = dp[i-2] + 2
# case 2: (())
# i-dp[i-1]-1 is the index of last "(" not paired until this ")"
elif i-dp[i-1]-1 >= 0 and s[i-dp[i-1]-1] == '(':
if dp[i-1] > 0: # content within current matching pair is valid
# add nearest parentheses pairs + 2 + parentheses before last "("
dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2]
else:
# otherwise is 0
dp[i] = 0
max_to_now = max(max_to_now, dp[i])
return max_to_now
64. 最小路径和
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m ,n = map(len, (grid, grid[0]))
for i in range(1,m):
grid[i][0] += grid[i-1][0]
for j in range(1, n):
grid[0][j] += grid[0][j-1]
for i in range(1, m):
for j in range(1, n):
grid[i][j] += min(grid[i-1][j],grid[i][j-1])
return grid[-1][-1]
91. 解码方法
class Solution:
def numDecodings(self, s: str) -> int:
if not s:
return 0
dp = [0 for _ in range(len(s) + 1)]
dp[0] = 1
dp[1] = 0 if s[0] == '0' else 1
for i in range(2, len(s) + 1):
if 0 < int(s[i-1:i]) <= 9:
dp[i] += dp[i-1]
if 10 <= int(s[i-2:i]) <=26:
dp[i] += dp[i-2]
return dp[-1]
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
if not matrix:
return 0
m, n = map(len, (matrix, matrix[0]))
dp = [[0] * (n+1) for _ in range(m+1)]
max_side = 0
for i in range(m):
for j in range(n):
if matrix[i][j] == '1':
dp[i+1][j+1] = min(dp[i][j+1], dp[i+1][j], dp[i][j]) + 1
max_side = max(max_side, dp[i+1][j+1])
return max_side * max_side
详细图文题解链接
363. 矩形区域不超过 K 的最大数值和
class Solution(object):
def maxSumSubmatrix(self, matrix, k):
# 计算前缀和的同时在出现过的前缀和中查找满足条件的值
# 二维前缀和
m, n = map(len, (matrix, matrix[0]))
pre=[[0]*(n+1)for _ in range(m+1)]
for i in range(m):
for j in range(n):
pre[i+1][j+1]=pre[i][j+1]+pre[i+1][j]+matrix[i][j]-pre[i][j]
def sumRange(x1,y1,x2,y2):
return pre[x2][y2]-pre[x1][y2]-pre[x2][y1]+pre[x1][y1]
res=float('-inf')
for i in range(n):
for j in range(i+1,n+1):
d=[0]
# 在一定宽度范围,累加每一行
# 当前累加和s来说,二分查找是否已经有过比s-k小的和
for l in range(1,m+1):
s=sumRange(0,i,l,j)
idx=bisect.bisect_left(d,s-k)
if idx<len(d):
res=max(res, s-d[idx])
bisect.insort(d,s)
return res
403. 青蛙过河
class Solution:
def canCross(self, stones: List[int]) -> bool:
# DP dynamic programming, DP table pi = dict()
# pi[i] = {distance of last jump when frog at stone pos i}
# Let n be the input size (size of stones)
# T(n) = O(n^2)
# S(n) = O(n^2)
# init data struct
# Time: O(n)
pi = dict()
for stone in stones:
pi[stone] = set()
# init state
# Time: O(1)
pi[0].add(0)
# fill DP table pi
# Time: O(n) * O(n) * O(1) = O(n^2)
for stone in stones:
for k in pi[stone]:
for jump in [k-1, k, k+1]:
if jump > 0 and stone + jump in pi:
pi[stone+jump].add(jump)
# return bool result
return len(pi[stones[-1]]) > 1
来源
简化逻辑如下:
class Solution:
def canCross(self, stones: List[int]) -> bool:
pi = {
stone:set() for stone in stones}
pi[0].add(0)
for stone in stones:
for k in pi[stone]:
for jump in [k-1, k, k+1]:
if jump > 0 and stone + jump in pi:
pi[stone+jump].add(jump)
return len(pi[stones[-1]]) > 0
410. 分割数组的最大值
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
# 指定二分查找范围
left, right = max(nums), sum(nums)
#定义 测试中点是大还是小的 测试函数
def test_mid(mid):
#初始化
num = 1 #num表示使用该mid我们会得到几个数组
cur_sum = 0 #s表示当前数组的和
for i in nums:
if cur_sum+i > mid: #如果当前数组已经超过mid,要停止这个数组
cur_sum = i #这个数变为下一个数组的开头
num += 1 #会得到的数组数量+1
else:
cur_sum += i
return num > m #数组总数是否>m, 大于的话说明mid太小,二分查找取右边
#这里有一个注意点,如果num已经等于m了, 但此时如果left不等于right,范围还是会继续收敛的,
#且取的是左半边,目的是让我们能最终找到一个确切的值,这个值恰好就是取得了最大值的那个数组的和
#(因为小于这个和的话,就不能通过num=m的测试;而大于这个m的话,即使通过了num=m的测试,
#范围也会继续向左边收敛,直到我们找到的就是这个和)。
#进行二分查找
while left < right: #当left == right的时候就终止查找,返回任意一个
mid = (left + right) // 2
is_right = test_mid(mid)
if is_right:
left = mid+1
else:
right = mid #num <= m的情况
return left
class Solution:
def leastInterval(self, tasks: List[str], n: int) -> int:
tasks_count = list(collections.Counter(tasks).values())
max_count = max(tasks_count)
max_count_tasks = tasks_count.count(max_count)
return max(len(tasks), (max_count - 1) * (n + 1) + max_count_tasks)
参考
647. 回文子串
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
count = 0
dp = [True]
for i in reversed(range(n)): # reversed 返回一个逆转的迭代器
next_dp = [True]
next_dp.append(True)
count += 1
for j in range(i + 1, n):
# substring length: j - i + 1 (>= 2)
if s[i] == s[j] and dp[j - i - 1]:
next_dp.append(True)
count += 1
else:
next_dp.append(False)
dp = next_dp
return count
from collections import Counter
class Solution:
def minWindow(self, s: str, t: str) -> str:
'''
Keep t_counter of char counts in t
We make a sliding window across s, tracking the char counts in s_counter
We keep track of matches, the number of chars with matching counts in s_counter and t_counter
Increment or decrement matches based on how the sliding window changes
When matches == len(t_counter.keys()), we have a valid window. Update the answer accordingly
How we slide the window:
Extend when matches < chars, because we can only get a valid window by adding more.
Contract when matches == chars, because we could possibly do better than the current window.
How we update matches:
This only applies if t_counter[x] > 0.
If s_counter[x] is increased to match t_counter[x], matches += 1
If s_counter[x] is increased to be more than t_counter[x], do nothing
If s_counter[x] is decreased to be t_counter[x] - 1, matches -= 1
If s_counter[x] is decreased to be less than t_counter[x] - 1, do nothing
Analysis:
O(s + t) time: O(t) to build t_counter, then O(s) to move our sliding window across s. Each index is only visited twice.
O(s + t) space: O(t) space for t_counter and O(s) space for s_counter
'''
if not s or not t or len(s) < len(t):
return ''
t_counter = Counter(t)
chars = len(t_counter.keys())
s_counter = Counter()
matches = 0
answer = ''
i = 0
j = -1 # make j = -1 to start, so we can move it forward and put s[0] in s_counter in the extend phase
while i < len(s):
# extend
if matches < chars:
# since we don't have enough matches and j is at the end of the string, we have no way to increase matches
if j == len(s) - 1:
return answer
j += 1
s_counter[s[j]] += 1
if t_counter[s[j]] > 0 and s_counter[s[j]] == t_counter[s[j]]:
matches += 1
# contract
else:
s_counter[s[i]] -= 1
if t_counter[s[i]] > 0 and s_counter[s[i]] == t_counter[s[i]] - 1:
matches -= 1
i += 1
# update answer
if matches == chars:
if not answer:
answer = s[i:j+1]
elif (j - i + 1) < len(answer):
answer = s[i:j+1]
return answer
参考