348.分隔数字串获取3的倍数问题 | 豆包MarsCode AI 刷题

0 阅读3分钟

问题描述

小Y有一个数字串,她希望通过分隔这个字符串来获得一些子串,每个子串代表一个数字。她的目标是最大化能获得的是 33 的倍数的数字的数量。分隔后的数字串不能包含前导零(但数字 00 本身是允许的),因为 00 也被视为 33 的倍数。

例如,对于数字串 1123,可以将其分割为 [1, 12, 3],其中 12 和 3 是 33 的倍数,因此小Y最多可以获得 2 个是 33 的倍数的数字。

解题思路

  1. 理解3的倍数的特性
  • 一个数是3的倍数,当且仅当它的各位数字之和是3的倍数。
  • 例如,数字 12 是3的倍数,因为 1 + 2 = 3,而3是3的倍数。
  1. 分割策略
  • 我们需要考虑如何分割数字串,使得每个子串的各位数字之和是3的倍数。
  • 分割时要注意不能包含前导零,除非子串本身就是 0
  1. 动态规划或贪心算法
  • 可以考虑使用动态规划来记录每个位置的最大3的倍数子串数量。
  • 或者使用贪心算法,尝试从左到右分割,确保每次分割的子串是3的倍数。
  1. 边界情况
  • 处理单个数字的情况,如 0 或 3
  • 处理整个数字串本身就是3的倍数的情况。
  1. 实现步骤
  • 遍历数字串,计算每个位置的数字和。
  • 尝试从每个位置分割,检查分割后的子串是否是3的倍数。
  • 记录并更新最大数量的3的倍数子串。

代码实现

def solution(n: str) -> int:
    dp = [0] * (len(n) + 1)

    for i in range(1, len(n) + 1):
        if int(n[i-1]) % 3 == 0:
            dp[i] = dp[i-1] + 1
        else:
            dp[i] = dp[i-1]

        current_sum = 0
        for j in range(i, 0, -1):
            current_sum += int(n[j-1])
            if current_sum % 3 == 0:
                dp[i] = max(dp[i], dp[j-1] + 1)

    return dp[len(n)]
    

注意

  1. 检查前导零:在尝试分割子串时,检查是否有前导零。如果 n[j-1] 是 '0',并且 j 不是 i(即不是当前字符),则跳过这个分割。
  2. 更新 dp 数组:只有在没有前导零的情况下,才更新 dp 数组。

时间复杂度分析

当前代码的主要时间复杂度来自于两个嵌套的循环:

  1. 外层循环:遍历字符串 n,时间复杂度为 O(n),其中 n 是字符串的长度。
  2. 内层循环:对于每个字符 i,内层循环从 i 遍历到 1,时间复杂度为 O(i)

因此,内层循环的总时间复杂度为:
[ \sum_{i=1}^{n} i = \frac{n(n+1)}{2} = O(n^2) ]

所以,整个算法的时间复杂度为 O(n^2)

空间复杂度分析

当前代码的空间复杂度主要来自于动态规划数组 dp,其长度为 n + 1,因此空间复杂度为 O(n)

总结

  • 时间复杂度O(n^2)
  • 空间复杂度O(n)

优化

def solution(n: str) -> int:
    dp = [0] * (len(n) + 1)
    prefix_sum = [0] * (len(n) + 1)

    for i in range(1, len(n) + 1):
        prefix_sum[i] = prefix_sum[i-1] + int(n[i-1])

    for i in range(1, len(n) + 1):
        if int(n[i-1]) % 3 == 0:
            dp[i] = dp[i-1] + 1
        else:
            dp[i] = dp[i-1]

        for j in range(i, 0, -1):
            current_sum = prefix_sum[i] - prefix_sum[j-1]
            if current_sum % 3 == 0 and (j == i or n[j-1] != '0'):
                dp[i] = max(dp[i], dp[j-1] + 1)

    return dp[len(n)]

优化后的复杂度分析

  • 时间复杂度O(n^2)(虽然仍然是 O(n^2),但减少了内层循环的计算量)
  • 空间复杂度O(n)(增加了前缀和数组的空间开销)