[Leetcode/python3] 第207场周赛

本文探讨了四个算法问题:重新排列字符串中的空格、拆分字符串以获得最多唯一子串、寻找矩阵中的最大非负积及连接两组点的最小成本。提供了详细的解决方案和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P1 重新排列单词间的空格

给你一个字符串 text ,该字符串由若干被空格包围的单词组成。每个单词由一个或者多个小写英文字母组成,并且两个单词之间至少存在一个空格。题目测试用例保证 text 至少包含一个单词 。

请你重新排列空格,使每对相邻单词之间的空格数目都 相等 ,并尽可能 最大化 该数目。如果不能重新平均分配所有空格,请 将多余的空格放置在字符串末尾 ,这也意味着返回的字符串应当与原 text 字符串的长度相等。

返回 重新排列空格后的字符串 。
Solution


class Solution:
    def reorderSpaces(self, cl text: str) -> str:
        words = []
        cur = ""
        cnt = 0
        for ch in text:
            if ch == ' ':
                cnt += 1
                if cur != "":
                    words.append(cur)
                cur = ""    
            else:    
                cur = cur + ch
        if cur != "":        
            words.append(cur)
        if len(words) == 1:        
            return words[0] + ' ' * cnt
        k = cnt // (len(words) - 1)        
        r = cnt - k * (len(words) - 1)
        ret = words[0]
        for x in words[1:]:
            ret += ' '*k + x
        ret += ' ' * r
        return ret

P2 拆分字符串使唯一子字符串的数目最大

给你一个字符串 s ,请你拆分该字符串,并返回拆分后唯一子字符串的最大数目。

字符串 s 拆分后可以得到若干 非空子字符串 ,这些子字符串连接后应当能够还原为原字符串。但是拆分出来的每个子字符串都必须是 唯一的 。

注意:子字符串 是字符串中的一个连续字符序列。
Solution

  1. 穷举
class Solution:
    def maxUniqueSplit(self, s: str) -> int:
        ret = 1
        n = len(s)
        def bfs(i, vis):
            nonlocal ret
            if i == n:
                ret = max(ret, len(vis))
                return 
            for j in range(i+1, n + 1):    
                if s[i:j] not in vis:
                    bfs(j, vis | {s[i:j]})
        bfs(0, set([]))            
        return ret

P3 矩阵的最大非负积

给你一个大小为 rows x cols 的矩阵 grid 。最初,你位于左上角 (0, 0) ,每一步,你可以在矩阵中 向右 或 向下 移动。

在从左上角 (0, 0) 开始到右下角 (rows - 1, cols - 1) 结束的所有路径中,找出具有 最大非负积 的路径。路径的积是沿路径访问的单元格中所有整数的乘积。

返回 最大非负积 对 109 + 7 取余 的结果。如果最大积为负数,则返回 -1 。

注意,取余是在得到最大积之后执行的。

Solution

  1. 穷举
  2. 可以优化只保留最大整数和最小负数
class Solution:
    def maxProductPath(self, grid: List[List[int]]) -> int:
        n, m = len(grid), len(grid[0])
        dp = [[set([]) for _ in range(m)] for _ in range(n)]
        for i in range(n):
            for j in range(m):
                if i == 0 and j == 0:
                    dp[i][j].add(grid[i][j])
                    continue    
                if i - 1 >= 0:    
                    for x in dp[i-1][j]:
                        dp[i][j].add(x * grid[i][j])
                if j - 1 >= 0:        
                    for x in dp[i][j - 1]:
                        dp[i][j].add(x * grid[i][j])
        ret = max(dp[-1][-1])
        return  -1 if ret < 0 else ret % (10 **9 + 7)

P4 连通两组点的最小成本

给你两组点,其中第一组中有 size1 个点,第二组中有 size2 个点,且 size1 >= size2 。

任意两点间的连接成本 cost 由大小为 size1 x size2 矩阵给出,其中 cost[i][j] 是第一组中的点 i 和第二组中的点 j 的连接成本。如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。

返回连通两组点所需的最小成本。

Solution

  1. 复杂度是 O ( n ∗ m ∗ 3 m ) O(n*m*3^m) O(nm3m)
  2. 枚举剩余状态的子集用v = mask & (v - 1)
class Solution:
    def connectTwoGroups(self, cost: List[List[int]]) -> int:
        n, m = len(cost), len(cost[0])
        cum_cost = [[0] * (1 << m) for _ in range(n)]
        for i in range(n):
            for state in range(1 << m):
                for j in range(m):
                    if state & (1 << j) > 0:
                        cum_cost[i][state] += cost[i][j]
                        
        inf = 10 ** 18
        dp = [[inf] * (1 << m) for _ in range(n)]
        dp[0] = cum_cost[0]             
        for i in range(1, n):
            for state in range(1, 1 << m):
                for j in range(m):
                    k = 1 << j
                    dp[i][state | k] = min(dp[i][state | k], dp[i-1][state] + cum_cost[i][k])
                rest = (1 << m) - 1 - state 
                v = rest
                while v > 0:
                    dp[i][state | v] = min(dp[i][state | v], dp[i-1][state] + cum_cost[i][v])
                    v = rest & (v - 1)
                    
        return dp[-1][-1]  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值