2896. Apply Operations to Make Two Strings Equal
from functools import cache
class Solution:
def minOperations(self, s1: str, s2: str, x: int) -> int:
'''
选i,j flip s1[i], s1[j] cost x
只选i, flip s1[i] s1[i+1] cost 1
store the list for s1 which needs to change [0, 3, 5, 8] for example 1
Now we can either:
1. move any index left or right for cost 1 (e.g. [0, 3, 5, 8] => [0, 4, 5, 8] for cost 1)
2. delete any two adjacent indexes for cost 1
3. delete any two non-adj indexes for cost x
add any two non-existing indexes for cost 1, but we wouldn't ever do this
Treat this problem like the classic house robbers DP problem.
1. Start from the last element in diffs: we can either pair this with the second to last element
and remove both of those for cost diffs[-1] - diffs[-2] (the distance between them),
or we can take out the last element for cost x / 2. Now repeat this process until you've removed all the elements in diffs!
Note that if there are an odd number of diffs, then it's impossible to correct them all, since every operation in the problem operates on exactly two bits. That means there must be an even number of diffs for any answer.
Thus, for the zeroth element, if we end up there, we have to pay the x / 2, since there's nothing to pair it with, and this is how we ensure that we always fully make pairs. (we never end up paying only x / 2 -- we'll always pay the x / 2 cost an even number of times)
'''
# DP O(n)
# indexes where the bits differ
diffs = [i for i, a, b in zip(range(len(s1)), s1, s2) if a != b]
# if there are an odd number of differences, it's impossible
if len(diffs) % 2 == 1: return -1
@cache
def lowestCostToIthDiff(i):
if i == 0:
return x / 2 # 和下面的x/2 匹配,一次是pair 2个去消掉, 为什么如果走了下面那个x/2的匹配,这里的x/2一定会用上?因为总共有偶数个diffs, 下面用到了1个,剩下odd个,这个0th也要拿出来,让rest成为even个数
if i == -1:
return 0
return min(
lowestCostToIthDiff(i - 1) + x / 2, # 和上面的x/2匹配, 便成x,因为一次是消掉2个
lowestCostToIthDiff(i - 2) + diffs[i] - diffs[i-1] # 这个也是匹配2个,但是是倒数两个
)
return int(lowestCostToIthDiff(len(diffs) - 1))
418. Sentence Screen Fitting
class Solution:
def wordsTyping(self, sentence: List[str], rows: int, cols: int) -> int:
'''
1. Based on the above observation, in the first for loop we compute the number of words that can be placed in the row if ith word is used as the starting word. This is saved as dp[i]. Note that this value can be greater than n.
2.In the next for loop we calculate how many words are placed in each row based on dp[i]. Imagine placing the 0th word in the row-0, then this row will hold dp[0] words. Next, which word will be placed on the start of next row? We calculate that using dp[k] % n (Remember dp[i] can be greater than n).
'''
n = len(sentence)
dp = [0 for _ in range(n)] # the number of workds can be fit in 1 line if start with word i
for i in range(n): # loop i to calcuate the dp[i] which starts with word i
cur_total_length = 0
number_of_words = 0
cur_index = i
while (cur_total_length + len(sentence[cur_index % n]) <= cols):
cur_total_length += len(sentence[cur_index % n])
cur_total_length += 1 # space
cur_index += 1
number_of_words += 1
dp[i] = number_of_words
# calcualte how many words can be fit into all rows
count = 0
index = 0
for r in range(rows):
count += dp[index]
index = (dp[index] + index) % n
return count // n
818. Race Car
class Solution:
def racecar(self, T: int, depth = 0) -> int:
# T > 0
q = collections.deque([[ 0, 1 ]])
seen = set('0,1')
while True:
k = len(q)
while k:
[ pos, vel ] = q.popleft()
if pos == T:
return depth # 🎯 target T found
cand = [] # next layer
if pos + vel > 0: # prune, check the dp idea, go back to somewhere>=0
cand.append([ pos + vel, 2 * vel ]) # A
cand.append([ pos, 1 if vel < 0 else -1 ]) # R
for pos, vel in cand:
if f'{pos},{vel}' not in seen:
q.append([ pos, vel ])
seen.add(f'{pos},{vel}')
k -= 1
depth += 1
return -1
'''
https://leetcode.com/problems/race-car/discuss/227415/Figures-to-make-the-DP-solution-more-straightforward
dp:
1. either go forward until pass the target and then go back
2. Or go forward until one step before target, then go back to somewhere >=0, then go forward again to the target.
'''
dp = {0: 0}
def racecar(self, t):
if t in self.dp:
return self.dp[t]
n = t.bit_length()
if 2**n - 1 == t: # 0, 1, 3, 7 = 2**n-1
self.dp[t] = n
else:
self.dp[t] = self.racecar(2**n - 1 - t) + n + 1 # n step + 1 R
for m in range(n - 1):
self.dp[t] = min(self.dp[t], self.racecar(t - 2**(n - 1) + 2**m) + n + m + 1) # link 里面的case1
return self.dp[t]
1014. Best Sightseeing Pair
class Solution:
def maxScoreSightseeingPair(self, values: List[int]) -> int:
# dp idea, we want to get the max value of A[i]+i + A[j]-j, where i<j, so we need to keep record of the previous best index which can make the max A[i]+i.
re = cur_max = 0
for j, n in enumerate(values):
re = max(re, values[j] - j + cur_max)
cur_max = max(cur_max, values[j] + j)
return re
继承了上题的思路的:
1937. Maximum Number of Points with Cost
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
'''
# dp[i][j] pick up i,j and the max points
# dp[i][j] = max(point[i, j] + previous row's dp[r][c] - diff)
R,C = len(points), len(points[0])
dp = [[points[i][j] for j in range(C)] for i in range(R)]
re = points[0][0]
for i in range(R):
for j in range(C): # O(N^3) time limited
if i != 0:
dp[i][j] = max(dp[i][j], points[i][j] + dp[i-1][pre_c] - abs(j-pre_c))
re = max(re, dp[i][j])
return re
'''
dp = [0] * len(points[0])
for line in points:
for i in range(1, len(line)):
dp[i] = max(dp[i], dp[i - 1] - 1) # 左边 最大的上一行的点-距离
dp[-i - 1] = max(dp[-i - 1], dp[-i] - 1) # 右边 最大的上一行的点 减去距离
for i in range(len(line)): # 这行的循环结束后 dp 给到 上面那个for循环,就相当于上一行的dp
dp[i] += line[i]
return max(dp)
1043. Partition Array for Maximum Sum
class Solution:
def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int:
# dp[i] will be the answer for array A[0], ..., A[i-1].
# For j = 1 .. k that keeps everything in bounds, dp[i] is the maximum of dp[i-j] # + max(A[i-1], ..., A[i-j]) * j .
N = len(arr)
dp = [0 for _ in range(len(arr) + 1)]
for i in range(N + 1):
cur_max = 0
for j in range(1, min(i, k)+1):
cur_max = max(cur_max, arr[i-j])
dp[i] = max(dp[i], dp[i-j] + cur_max*j)
return dp[N]
354. Russian Doll Envelopes
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
'''
先按照宽度排序,再按照长度排序,如果宽度一样,我们把长度长的排在前面,然后根据长度做longest increasement sequence
因为等宽的不能放进一个信封,所以选了4,8 就不能选4,6了。 如果4,6在4,8前面,LIS就会计算4,6和4,8
[2,3]
[3,7]
[4,8]
[4,6]
'''
env = sorted(envelopes, key=lambda x: (x[0], -x[1]))
dp = [1] * len(env)
for i, (_, y) in enumerate(env):
for j in range(i):
if env[j][1] < y:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) if dp else 0
''' 一样也是LIS 但是更好的方法
envs = sorted(envelopes, key=lambda x: (x[0], -x[1]))
res =[float('inf')] * len(envs)
for _, h in envs:
res[bisect.bisect_left(res, h)] = h
return bisect.bisect_left(res, float('inf'))
'''
44. Wildcard Matching
func isMatch(s string, p string) bool {
dp := make([][]bool, len(s)+1) // dp[s_idx][p_idx]
for i := range dp {
dp[i] = make([]bool, len(p)+1)
}
// init dp start from the table initiated as False everywhere but dp[0][0] = True.
dp[0][0] = true
// above miss to compute the situation when s is '' but p is not
for j := 1; j <= len(p); j++ {
if p[j-1] == '*' {
dp[0][j] = dp[0][j-1]
}
}
// apply two rules
// 1. if s[s_idx-1] = p[p_idx-1] dp[i][j] i: idx of s, j: idx of p = dp[i-1][j-1]
// 2. if p[p_idx-1] = '*' and dp[i][j-1] = true or dp[i-1][j-1], dp[idx][j] = true for all idx >= i-1
// case: s = a p = a* dp[1][2] = dp[1][1], s = a p = * dp[1][1] = dp[0][1]
for i := 1; i <= len(s); i++ {
for j := 1; j <= len(p); j++ {
if dp[i][j] == true { // because of 下面的 rule2
continue
}
if s[i-1] == p[j-1] || p[j-1] == '?'{ // rule1
dp[i][j] = dp[i-1][j-1]
}else if p[j-1] == '*' { // rule2
if dp[i][j-1] == true || dp[i-1][j] == true {
for idx := i; idx <= len(s); idx++ {
dp[idx][j] = true
}
}
}
}
}
return dp[len(s)][len(p)]
}
func isMatch(s string, p string) bool {
// mem[i][j] means isMatch(s[:i], p[:j])
mem := make([][]bool, len(s)+1)
for i := range mem {
mem[i] = make([]bool, len(p)+1)
}
// init bound, mem[n][0] is false while n > 0
mem[0][0] = true
for j := 1; j <= len(p); j++ {
if p[j-1] == '*' {
mem[0][j] = mem[0][j-1]
}
}
for i := 1; i <= len(s); i++ {
for j := 1; j <= len(p); j++ {
if p[j-1] == '*' {
// mem[i][j] = mem[i][j-1] 则* 匹配空字符串, =mem[i-1][j]则j匹配任意当前字符串
mem[i][j] = mem[i][j-1] || mem[i-1][j]
} else if p[j-1] == '?' || p[j-1] == s[i-1] {
mem[i][j] = mem[i-1][j-1]
}
}
}
return mem[len(s)][len(p)]
}
410. Split Array Largest Sum
参考
func splitArray(nums []int, m int) int {
// dp
length := len(nums)
dp := make([][]int, length+1)
// init dp[][]
for i := 0; i <= length; i++ {
dp[i] = make([]int, m+1)
for j := 0; j <= m; j++ {
dp[i][j] = math.MaxInt32
}
}
preSum := make([]int, length+1)
for i, v := range nums {
preSum[i+1] = preSum[i] + v
}
dp[0][0] = 0
for i:= 1; i <= length; i++ {
for j := 1; j <= m; j++ {
for k := 0; k < i; k++ { // 0 <= k < i
// dp[i][j] nums[0]~nums[i] split to j parts
dp[i][j] = min(dp[i][j], max(dp[k][j-1], preSum[i] - preSum[k]))
}
}
}
return dp[length][m]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a < b {
return b
}
return a
}
312. Burst Balloons
https://www.youtube.com/watch?v=z3hu2Be92UA&ab_channel=HuaHua
dp[i][j] in here means, the maximum coins we get after we burst all the balloons between i+1 and j-1 in the array
class Solution(object):
def maxCoins(self, nums):
nums = [1] + nums + [1]
n = len(nums)
dp = [[0] * n for _ in xrange(n)]
print n
for gap in range(2, n):
for left in range(0, n-gap):
right = left + gap
for i in range(left+1, right): # left, right are not included for the last burst, they are the edges
dp[left][right] = max(dp[left][right],
nums[left]*nums[i]*nums[right] + dp[left][i] + dp[i][right])
return dp[0][-1]
1000. Minimum Cost to Merge Stones
花花酱 LeetCode 1000. Minimum Cost to Merge Stones – Huahua’s Tech Road
class Solution(object):
def mergeStones(self, stones, K):
n = len(stones)
if (n-1) % (K-1): return -1
inf = float('inf')
preSum = [0] * (n+1)
for i in xrange(n):
preSum[i+1] = stones[i] + preSum[i]
# dp[i][j][k] := min cost to merge subarray i~j into k piles.
dp = [[[inf] * (K+1) for _ in xrange(n)] for _ in xrange(n)]
for i in xrange(n):
dp[i][i][1] = 0
for l in xrange(2, n+1): # subproblem length
for i in xrange(n-l+1):
j = i + l - 1
for k in xrange(2, K+1):
for m in xrange(i, j):
dp[i][j][k] = min(dp[i][j][k], dp[i][m][1] + dp[m+1][j][k-1])
if dp[i][j][K] < inf:
dp[i][j][1] = dp[i][j][K] + preSum[j+1] - preSum[i]
return dp[0][-1][1]
446. Arithmetic Slices II - Subsequence
class Solution(object):
def numberOfArithmeticSlices(self, A):
# dp[i][j] the number of arithmetic ending with idx ith, increment is j
dp = [collections.defaultdict(int) for _ in A]
re = 0
for i in xrange(len(A)):
for j in xrange(i):
dp[i][A[i]-A[j]] += 1 # 把2个的也加入
if A[i]-A[j] in dp[j]:
dp[i][A[i]-A[j]] += dp[j][A[i]-A[j]] # dp[j]是至少两个的, dp[i]是比dp[j]多一个的, 因为上面加1了,所以这里不加1
re += dp[j][A[i]-A[j]] # dp[j][A[i]-A[j]] + 1 - 1
return re
329. Longest Increasing Path in a Matrix
class Solution(object):
def longestIncreasingPath(self, matrix):
def dfs(i, j):
if not dp[i][j]:
val = matrix[i][j]
dp[i][j] = 1 + max(dfs(i-1, j) if i > 0 and matrix[i-1][j] < val else 0, # dfs不是dp
dfs(i+1, j) if i+1 < len(matrix) and matrix[i+1][j] < val else 0,
dfs(i, j-1) if j > 0 and matrix[i][j-1] < val else 0,
dfs(i, j+1) if j+1 < len(matrix[0]) and matrix[i][j+1] < val else 0)
return dp[i][j]
if not matrix or not matrix[0]: return 0
dp = [[0] * len(matrix[0]) for _ in xrange(len(matrix))]
return max(dfs(i, j) for i in xrange(len(matrix)) for j in xrange(len(matrix[0])))
518. Coin Change 2
class Solution(object):
def change(self, amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
dp = [0] * (amount+1)
dp[0] = 1 # so that dp[amou-coin=0] = 1
for c in coins:
for amou in xrange(1, amount+1):
if amou >= c:
dp[amou] += dp[amou-c]
return dp[amou]
322. Coin Change
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
dp = collections.defaultdict(int)
for money in xrange(1, amount+1):
minCoins = 5000
for coin in coins:
if money-coin>=0 and dp[money-coin] != -1:
minCoins = min(dp[money-coin], minCoins)
if minCoins == 5000:
dp[money] = -1
else:
dp[money] = minCoins + 1
return dp[amount]
174. Dungeon Game
第一次解,超时:
class Solution(object):
def dfs(i, j, val, minVal):
val += d[i][j]
if i == len(d) - 1 and j == len(d[0])-1:
return minVal
rightward = float('-inf')
if j + 1 <= len(d[0]) - 1 :
rightward = dfs(i, j+1, val, minVal)
downward = float('-inf')
if i + 1 <= len(d) - 1:
downward = dfs(i+1, j, val, minVal)
return max(rightward, downward)
re = dfs(0, 0, 0, 0)
return -re + 1 if re <= 0 else 1
DP, 自底向上
class Solution(object):
def calculateMinimumHP(self, d):
# hp[i][j] to store the min hp needed at position (i, j)
# adding dummy row and column to hp would make the code cleaner
r, c = len(d), len(d[0])
hp = [[sys.maxint] * (c+1) for _ in xrange(r+1)]
hp[r][c-1] = 1
hp[r-1][c] = 1
for i in xrange(r-1, -1, -1):
for j in xrange(c-1, -1, -1):
need = min(hp[i+1][j], hp[i][j+1]) - d[i][j]
# 这里负数要设为1,因为如左下角10往上走的时候,才会是1+5+2
hp[i][j] = need if need > 0 else 1
return hp[0][0]
213. House Robber II
dp, 首尾是环,转换成2部分
- Rob houses
0
ton - 2
; - Rob houses
1
ton - 1
.
class Solution(object):
def rob(self, nums):
if len(nums) == 1:
return nums[0]
def robHouse(nums, low, high):
pre2 = pre1 = 0
cur = 0
for i in xrange(low, high):
cur = max(pre1, pre2+nums[i])
pre2 = pre1
pre1 = cur
return cur
return max(robHouse(nums, 0, len(nums)-1), robHouse(nums, 1, len(nums)))
368. Largest Divisible Subset
LIS算法的变形,原理一样
class Solution(object):
def largestDivisibleSubset(self, nums):
if not nums:
return []
nums = sorted(nums)
dp = [[nums[0]] for i in nums]
res = [nums[0]]
for i in xrange(1, len(nums)):
dp[i] = [nums[i]]
for j in xrange(0, i):
if nums[i] % nums[j] == 0:
dp[i] = dp[j] + [nums[i]] if len(dp[j]) >= len(dp[i]) else dp[i]
res = dp[i] if len(res) < len(dp[i]) else res
return res
263. Ugly Number
给ugly Number 2做铺垫
class Solution(object):
def isUgly(self, num):
rest = num
if rest == 0:
return False
while rest != 1:
if rest % 2 == 0:
rest /= 2
elif rest % 3 == 0:
rest /= 3
elif rest % 5 == 0:
rest /= 5
else:
return False
return True
别人的写法:汗颜,学习了。 num%p == 0 < num 相当于 Num%p == 0 and num%p < num
for p in 2, 3, 5:
while num % p == 0 < num:
num /= p
return num == 1
264. Ugly Number II
Explanation:
The key is to realize each number can be and have to be generated by a former number multiplied by 2, 3 or 5
e.g.
1 2 3 4 5 6 8 9 10 12 15..
what is next?
it must be x * 2 or y * 3 or z * 5, where x, y, z is an existing number.
How do we determine x, y, z then?
apparently, you can just traverse the sequence generated by far from 1 ... 15, until you find such x, y, z that x * 2, y * 3, z * 5 is just bigger than 15. In this case x=8, y=6, z=4. Then you compare x * 2, y * 3, z * 5 so you know next number will be x * 2 = 8 * 2 = 16.
k, now you have 1,2,3,4,....,15, 16,
Then what is next?
You wanna do the same process again to find the new x, y, z, but you realize, wait, do I have to
traverse the sequence generated by far again?
NO! since you know last time, x=8, y=6, z=4 and x=8 was used to generate 16, so this time, you can immediately know the new_x = 9 (the next number after 8 is 9 in the generated sequence), y=6, z=4.
Then you need to compare new_x * 2, y * 3, z * 5. You know next number is 9 * 2 = 18;
And you also know, the next x will be 10 since new_x = 9 was used this time.
But what is next y? apparently, if y=6, 6*3 = 18, which is already generated in this round. So you also need to update next y from 6 to 8.
Based on the idea above, you can actually generated x,y,z from very beginning, and update x, y, z accordingly. It ends up with a O(n) solution.
class Solution(object):
def nthUglyNumber(self, n):
dp = [1]*(n+1)
n2 = n3 = n5 = 1
for i in xrange(2, n+1):
dp[i] = min(dp[n2]*2, dp[n3]*3, dp[n5]*5)
if dp[n2]*2 == dp[i]:
n2 += 1
if dp[n3]*3 == dp[i]:
n3 += 1
if dp[n5]*5 == dp[i]:
n5 += 1
return dp[n]
467. Unique Substrings in Wraparound String
思路:
- The max number of unique substring ends with a letter equals to the length of max contiguous substring ends with that letter. Example
"abcd"
, the max number of unique substring ends with'd'
is 4, apparently they are"abcd", "bcd", "cd" and "d"
. - If there are overlapping, we only need to consider the longest one because it covers all the possible substrings. Example:
"abcdbcd"
, the max number of unique substring ends with'd'
is 4 and all substrings formed by the 2nd"bcd"
part are covered in the 4 substrings already. - No matter how long is a contiguous substring in
p
, it is ins
sinces
has infinite length. - Now we know the max number of unique substrings in
p
ends with'a', 'b', ..., 'z'
and those substrings are all ins
. Summary is the answer, according to the question.
class Solution(object):
def findSubstringInWraproundString(self, p):
dp = [0] * 26
re = 0
pre = 0
for i in xrange(len(p)):
if (ord(p[i]) - ord(p[i-1])) in (1, -25):
cur = pre + 1
else:
cur = 1
pre = cur
val = ord(p[i])-ord('a')
dp[val] = max(dp[val], cur)
return sum(dp)
139. Word Break
思路:
class Solution(object):
def wordBreak(self, s, wordDict):
dp = [False] * len(s)
for i in xrange(0, len(s)):
for w in wordDict:
if i-len(w)+1 >= 0:
if w == s[i-len(w)+1: i+1] and (dp[i-len(w)] or i-len(w) == -1):
dp[i] = True
return dp[-1]
BFS
I use BFS to avoid useless states calculation like someone did in Coin Change. I do not check every substring but I check the substring whose length is possible (I store all distinct length of words in a list). Thus, no need to check backward from the current position one by one.
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: Set[str]
:rtype: bool
"""
queue = [0]
slen = len(s)
lenList = [l for l in set(map(len,wordDict))]
visited = [0 for _ in range(0, slen + 1)]
while queue:
tmpqueue = []
for start in queue:
for l in lenList:
if s[start:start+l] in wordDict:
if start + l == slen:
return True
if visited[start + l] == 0:
tmpqueue.append(start+l)
visited[start + l] = 1
queue, tmpqueue = tmpqueue, []
return False
673. Number of Longest Increasing Subsequence
Given an unsorted array of integers, find the number of longest increasing subsequence.
Example 1:
Input: [1,3,5,4,7]
Output: 2
Explanation: The two longest increasing subsequence are [1, 3, 4, 7] and [1, 3, 5, 7].
Example 2:
Input: [2,2,2,2,2]
Output: 5
Explanation: The length of longest continuous increasing subsequence is 1, and there are 5 subsequences' length is 1, so output 5.
思路:
The idea is to use two arrays len[n]
and cnt[n]
to record the maximum length of Increasing Subsequence and the coresponding number of these sequence which ends with nums[i]
, respectively. That is:
len[i]
: the length of the Longest Increasing Subsequence which ends with nums[i]
.cnt[i]
: the number of the Longest Increasing Subsequence which ends with nums[i]
.
Then, the result is the sum of each cnt[i]
while its corresponding len[i]
is the maximum length.
class Solution(object):
def findNumberOfLIS(self, nums):
if not nums:
return 0
count, dp = [0 for i in nums], [1 for i in nums]
for i in xrange(len(nums)):
for j in xrange(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
for j in xrange(i):
if nums[j] < nums[i] and dp[i]-1 == dp[j]:
count[i] += count[j] if count[j] else 1
res = 0
for i in xrange(len(nums)):
if dp[i] == max(dp):
res += count[i] if count[i] else 1
return res
576. Out of Boundary Paths
Note:
- Once you move the ball out of boundary, you cannot move it back.
- The length and height of the grid is in range [1,50].
- N is in range [0,50].
- 这道题乍一看很像一个标准的bfs,因为限定最多只能移动N次,我们只要bfs依次遍历发现出界就+1,当bfs的深度大于N的时候break。当然理论上是没有任何问题的,确实能得出正确答案,但是这里N的取值范围达到了50,我们对任意一个点bfs有四个方向(可以走回头路),那么复杂度达到了4^N,显然会超时。当然我会在文章后面给出bfs的做法,毕竟这是可以处理N比较小的情况的解法,让大家更熟悉bfs的套路。
- 我不知道你们有没有这种感觉,一般看到这个mod 1e9+7,这道题8成就是dp了,而且就是那种每个dp值你都得mod一下再去进行运算的那种。我觉得这算一个小技巧吧,看到mod 1e9+7就要想到dp。
- 显然,这里dp很好定义,我们定义
dp[(i,j,N)]
表示从i,j出发,最多走N步情况下满足题意的路径数量
,那么我们所求也就是dp[(i,j,N)]。根据我们上面说的bfs的思路,递推式可得:dp[(i,j,N)] = dp[(i+1,j,N-1)] + dp[(i-1,j,N-1)] + dp[(i,j+1,N-1)] + dp[(i,j-1,N-1)]
class Solution(object):
def findPaths(self, m, n, N, i, j):
"""
:type m: int
:type n: int
:type N: int
:type i: int
:type j: int
:rtype: int
"""
mod = 10**9 + 7
cache = collections.defaultdict(int)
def helper(N, i, j, cache):
if (i, j, N) in cache:
return cache[(i, j, N)]
if 0<=i<m and 0<=j<n:
if N == 0:
cache[(i, j, N)] = 0
return 0
for x, y in ((i-1, j), (i+1, j), (i, j-1), (i, j+1)):
cache[(i, j, N)] += helper(N-1, x, y, cache)
return cache[(i, j, N)] % mod
else:
cache[(i, j, N)] = 1
return 1
return helper(N, i, j, cache)
| |
787. Cheapest Flights Within K Stops
Dijkstra's algorithm 每次更新剩余点到起始点的价格。
There are n
cities connected by m
flights. Each fight starts from city u
and arrives at v
with a price w
.
Now given all the cities and fights, together with starting city src
and the destination dst
, your task is to find the cheapest price from src
to dst
with up to k
stops. If there is no such route, output -1
.
Example 1: Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 1 Output: 200 Explanation: The graph looks like this: The cheapest price from city0
to city2
with at most 1 stop costs 200, as marked red in the picture.
Example 2: Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 0 Output: 500 Explanation: The graph looks like this: The cheapest price from city0
to city2
with at most 0 stop costs 500, as marked blue in the picture.
class Solution(object):
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
"""
prices = collections.defaultdict(dict)
for a, b, p in flights:
prices[a][b] = p
heap = [(0, src, K+1)]
while heap:
price, curPlace, steps = heapq.heappop(heap)
if curPlace == dst:
return price
if steps > 0:
for arr in prices[curPlace]:
heapq.heappush(heap, (price+prices[curPlace][arr], arr, steps-1))
return -1
304. Range Sum Query 2D - Immutable
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).
The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8.
Example:
Given matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]
sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
class NumMatrix(object):
def __init__(self, matrix):
"""
:type matrix: List[List[int]]
"""
dpSum = [[0]*len(matrix[0]) for i in matrix]
for i in xrange(len(matrix)):
for j in xrange(len(matrix[0])):
if j-1>=0:
dpSum[i][j] += dpSum[i][j-1]
for row in xrange(0, i+1):
dpSum[i][j] += matrix[row][j]
self.dpSum = dpSum
def sumRegion(self, row1, col1, row2, col2):
"""
:type row1: int
:type col1: int
:type row2: int
:type col2: int
:rtype: int
"""
res = self.dpSum[row2][col2]
if col1 - 1 >= 0:
res -= self.dpSum[row2][col1-1]
if row1 -1 >= 0:
res -= self.dpSum[row1-1][col2]
if col1 - 1 >= 0 and row1 -1 >= 0:
res += self.dpSum[row1-1][col1-1]
return res
# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)
464. Can I Win
游戏的可以整理一篇,都是暴力破解(递归)加记忆。DP用于记录计算过的路径避免重复计算。
class Solution(object):
def canIWin(self, maxChoosableInteger, desiredTotal):
choose = tuple(i for i in xrange(1, maxChoosableInteger+1))
visited = {}
if sum(choose) < desiredTotal: return False
def helper(target, choose, visited):
if len(choose) == 0:
return False
if choose in visited: return visited[choose]
if target - max(choose) <= 0:
visited[choose] = True
return True
for n in choose:
leftChoose = tuple(x for x in choose if x!=n)
# 对方输,我方就赢
if not helper(target-n, leftChoose, visited):
visited[choose] = True
return True
visited[choose] = False
return False
return helper(desiredTotal, choose, visited)
523. Continuous Subarray Sum
思路:
暴力破解就不说了,
在讨论里有个大神给出了时间复杂度是O(nn)的解法,他的思路非常巧妙,用了数学上的知识,下面给出他的解法的原理:
假设:
a[i]+a[i+1]+...+a[j]=n1k+q;a[i]+a[i+1]+...+a[j]=n1k+q;
如果存在一个n
n>j且a[i]+a[i+1]+...+a[j]+...+a[n]=n2k+q;n>j且a[i]+a[i+1]+...+a[j]+...+a[n]=n2k+q;
那么
a[j+1]+...+a[n]=(n2−n1)ka[j+1]+...+a[n]=(n2−n1)k
因此利用这一结果,可以从序列第一个元素开始遍历,不断累加上当前的元素,并求出当前和除以k后的余数,用一个映射记录该余数出现时的下标,如果同一个余数出现了两次,并且两次出现的下标之差大于1,那么就表示在这两个坐标之间的元素之和是k的倍数,因此就可以返回true,否则最后返回false。
需要注意的两个地方:
1. k可能取0,所以只有当k不为0时才对当前的和求余,同时针对于nums = [0, 0], k = 0的情况,需要添加一个初始映射(0, -1)来确保结果的正确。
2. 下标之差至少为2才算是正确的情况,因为题目要求子序列长度至少为2,以上面的例子就是n至少等于j+2。
具体实现见下面参考代码。
class Solution(object):
def checkSubarraySum(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
if len(nums) < 2:
return False
if k == 0:
for i in range(0, len(nums) - 1):
if nums[i] == 0 and nums[i+1] == 0:
return True
return False
k = abs(k)
if len(nums) >= 2*k:
return True
Sum = [0]
for x in nums:
Sum.append((Sum[-1]+x)%k)
Dict = {}
for i in xrange(len(Sum)):
if Dict.has_key(Sum[i]):
if i - Dict[Sum[i]] >= 2:
return True
else:
Dict[Sum[i]] = i
return False
91. Decode Ways
A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given a non-empty string containing only digits, determine the total number of ways to decode it.
Example 1:
Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).
Example 2:
Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).
i指的是字符串长度。
class Solution(object):
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if s[0] == '0':
return 0
dp = [0 for i in xrange(len(s)+1)]
dp[0] = 1
dp[1] = 0 if s[0] == '0' else 1
for i in xrange(2, len(s)+1):
one = int(s[i-1])
two = int(s[i-2:i])
if 1 <= one:
dp[i] = dp[i-1]
if 10 <= two <= 26:
dp[i] += dp[i-2]
return dp[-1]
639. Decode Ways II
等于切出最后一位和最后2位,看1位的几种和2位的几种 分别乘以对应的dp[i-1], dp[i-2]
这题的解法上一题也可以用。
** = 11-19, 21-26. 如果** 是比如 37的话,应该是在one里面
class Solution(object):
def numDecodings(self, s):
one = {'0': 0, '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1, '8': 1, '9': 1, '*': 9}
two = {'10': 1, '11': 1, '12': 1, '13': 1, '14': 1, '15': 1, '16': 1, '17': 1, '18': 1, '19': 1, '20': 1, '21': 1,
'22': 1, '23': 1, '24': 1, '25': 1, '26': 1, '*0': 2, '*1': 2, '*2': 2, '*3': 2, '*4': 2, '*5': 2, '*6': 2,
'*7': 1, '*8': 1, '*9': 1, '1*': 9, '2*': 6, '**': 15}
dp = [1, one.get(s[0])] # dp[i-2], dp[i-1]
for i in xrange(1, len(s)):
dp = dp[1], (one.get(s[i]) * dp[1] + two.get(s[i-1: i+1], 0) * dp[0]) % 1000000007
return dp[-1]