python partition函数_LeetCode Partition专题(Python)

Partition类题目分为两类

(如果时间有限,可先刷带加粗的题)隔板划分:比如在19216811这串数字中找到所有有效地IP地址划分,输入顺序固定Medium: 93, 131,842

Hard: 282

2.桶分类:给一个数组[2, 2, 3, 3] 问是否可以分成和相同的两部分,顺序可以打乱Medium: 198

两类问题都使用backtracking回溯法的方法实现,首先来看131题

这题需要按隔板划分一个str,并且每一个partition都要求是回文,可用递归搜索实现,每次找到符合要求的划分后记录,实现如下

def partition(self, s: str) -> List[List[str]]:

def isPali(s):

return s == s[::-1]

# s - str to search, path - previous result

def dfs(s, path):

if not s: yield path

for i, _ in enumerate(s):

cur_str = s[: i + 1]

if isPali(cur_str):

yield from dfs(s[i + 1: ], path + [cur_str])

return list(dfs(s, []))

N - length of input string

Time: O(2^N * N)

Space: O(N)

时间复杂度上,由于搜索会遍历所有划分,那么输入长度为n的str,每一个间隔都可放隔板或不放,带来2^N种可能,每次判断是否为回文需要N

空间上,递归最深为N,另外不需要考虑答案数量带来的空间复杂度,因为这是不能被解法影响的,所以假装他是常数吧

这题为这种类型最基本的模板,下一题非常类似,只是改了substring判断条件和一些细节

def restoreIpAddresses(self, s: str) -> List[str]:

def is_valid(s: str) -> bool:

if not len(s) in range(1,4): return False

if s[0] == '0' and len(s) > 1: return False

return int(s) in range(0, 256)

def search(s: str, path: List[str]):

if len(path) > 4: return

if s == "" and len(path) == 4:

yield functools.reduce(lambda str1, str2: f"{str1}.{str2}", path)

# Try all possible next ip part

for i in range(min(3, len(s))):

head = s[: i + 1]

if is_valid(head):

yield from search(s[i + 1: ], path + [head])

else:

return

return list(search(s, []))

Time: O(1)

Space: O(1)

有效IP最长12位,递归深度和划分总数都是常数

下一题稍为复杂,递归函数内还要附加信息,之前的暂存搜索结果也成为判断条件的一部分

def splitIntoFibonacci(self, S: str) -> List[int]:

def search(S: str, pre: int, cur: int, path: List[int]):

# If found: save and return

if not S:

if pre != None and cur != None and len(path) >= 3:

yield path

return

# search forward

# Only read 0 or 1 numbers

elif pre == None:

for i, _ in enumerate(S):

# Leading zeros are not allowed

if i > 0 and S[0] == '0': return

head = int(S[:i + 1])

yield from search(S[i + 1: ], cur, head, path + [head])

# Have read >= 2 numbers

else:

expected = str(pre + cur)

# F[i] <= 2**31 - 1

if int(expected) > 2**31 - 1: return

head = S[: len(expected)]

if head == expected:

yield from search(S[len(expected): ], cur, pre + cur, path + [pre + cur])

return next(search(S, None, None, []), [])

N - length of input string

Time: O(N^2)

Space: O(N)

因为只要确定下前两个数,接下来就可以用N时间判断整个str是否满足条件了,确定前两个数需要N^2

空间上递归最深是O(N)层,也只需要返回一个符合的partition

下一题比这题在调用递归函数的条件判断更复杂

def addOperators(self, num: str, target: int) -> List[str]:

def dfs(num: str, prev: int, cur: int, goal: int, path: str):

if not num:

if goal == target and cur == 0:

yield path[1:]

return

# Extend

cur = cur * 10 + int(num[0])

# Avoid starting 0

if cur > 0:

yield from dfs(num[1:], prev, cur, goal, path)

# Addition

yield from dfs(num[1:], cur, 0, goal + cur, f"{path}+{cur}")

# Can * or - only if there are some prev operands

if path:

# -

yield from dfs(num[1:], -cur, 0, goal - cur, f"{path}-{cur}")

# *

yield from dfs(num[1:], cur * prev, 0, goal - prev + (cur * prev), f"{path}*{cur}")

return list(dfs(num, 0, 0, target, ""))

这题思路上非常拧巴,需要一边搜索一边帮目前的表达式估值

dfs(num, prev, cur, goal, path)num:剩余待搜索的字符串

prev: 上一次被加上的值

cur:当前数字的值

goal: 目前表达式的值

path: 记录的待输出结果

重点是prev,当遇到乘法,因为优先级更高,需要退回之前被加的值,重新计算再往前搜索

这题为桶划分类型,与隔板划分略有不同。在尝试把nums[i]加入某个桶中过程中,在往前搜索时i总是递增的,板划分类的题因为具有这个特性,所以只需要往前搜索-返回,不需要额外记录状态。这题因为尝试放进桶的编号不是随着搜索往前一直增加的,所以像通常回溯法一样:先尝试走一步,记录状态 -> 往前搜索 -> 退回

def canPartitionKSubsets(self, nums, k):

subsum, remainder = divmod(sum(nums), k)

if remainder != 0: return False # Prune1

nums.sort(reverse = True) # Prune2: Pick larger nums first

buckets = [0] * k # Sum of nums in a subset

# Try to add nums[index] to buckets

def search(index: int) -> bool:

if index == len(nums):

return len(set(buckets)) == 1

for i, val in enumerate(buckets):

buckets[i] += nums[index] # Try to add

if buckets[i] <= subsum and search(index + 1): # Search Forward

return True

buckets[i] = val # Revert

# Prune3: If nums[index] can't be added to either, then no valid partition

if buckets[i] == 0: return False

return search(0)

N = len(nums)

Time complexity: O(K^N)

Space complexity: O(N)

时间复杂度即每次有K种放法,一共要放N次。可以依靠各种剪枝优化

空间复杂度为递归深度N

这系列文章希望总结LeetCode中不同类型题目的共通规律,模板

参考了刷题界思想领袖花花酱的题目分类。可能不完全,之后会持续更新

本系列其他文章麦当劳No1:LeetCode二分查找专题1 易于理解的边界详解(Python)​zhuanlan.zhihu.comzhihu-card-default.svg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值