刷题打卡 | 豆包MarsCode AI刷题一

93 阅读12分钟
  1. 数字分组求偶数和

问题描述

小M面对一组从 1 到 9 的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。

  • numbers: 一个由多个整数字符串组成的列表,每个字符串可以视为一个数字组。小M需要从每个数字组中选择一个数字。

例如对于[123, 456, 789],14个符合条件的数为:147 149 158 167 169 248 257 259 268 347 349 358 367 369

思路

  1. 初始化even_count 用于记录形成偶数和的组合数,odd_count 用于记录形成奇数和的组合数。初始状态下,没有选择任何数字,所以有一个选择,即不选择任何数字,因此 even_count 初始化为 1。

  2. 遍历数字:对于输入列表 numbers 中的每个数字,代码会遍历该数字的每一位。

  3. 统计偶奇数位:对于当前数字的每一位,代码会判断它是偶数还是奇数,并相应地增加 evenodd 计数器。

  4. 更新组合数:根据当前数字的偶奇位统计结果,代码会更新 even_countodd_count。这里使用了两个公式:

    • new_even_count = even_count * even + odd_count * odd:这个公式表示,要形成一个新的偶数和,可以通过以下两种方式:

      • 将当前数字的偶数位与之前形成的偶数和组合。
      • 将当前数字的奇数位与之前形成的奇数和组合。
    • new_odd_count = even_count * odd + odd_count * even:这个公式表示,要形成一个新的奇数和,可以通过以下两种方式:

      • 将当前数字的偶数位与之前形成的奇数和组合。
      • 将当前数字的奇数位与之前形成的偶数和组合。
  5. 迭代更新:对于列表中的每个数字,都会根据上述规则更新 even_countodd_count

  6. 返回结果:最后,函数返回 even_count,即形成偶数和的所有可能组合数。

代码

def solution(numbers):
    even_count = 0  # 记录偶数选择的组合数  
    odd_count = 0   # 记录奇数选择的组合数  
 
    # 初始情况下,没有任何选择,所以组合数为0
    even_count = 1  # 初始化为1,因为至少有一种选择(即不选择任何数字)
 
    for num in numbers:
        even = 0
        odd = 0
        str_num = str(num)
 
        # 统计当前数字组中的偶数和奇数  
        for ch in str_num:
            if int(ch) % 2 == 0:
                even += 1
            else:
                odd += 1
 
        # 更新组合数  
        new_even_count = even_count * even + odd_count * odd  # 偶数和偶数的组合 + 奇数和奇数的组合
        new_odd_count = even_count * odd + odd_count * even    # 偶数和奇数的组合
 
        even_count = new_even_count
        odd_count = new_odd_count
 
    # 返回符合条件的组合数(偶数和的组合)  
    return even_count  # 返回偶数和的组合数
 
# 测试用例
if __name__ == "__main__":
    print(solution([123, 456, 789]) == 14)  # 应输出 True
    print(solution([123456789]) == 4)       # 应输出 True
    print(solution([14329, 7568]) == 10)    # 应输出 True
  1. DNA序列编辑距离

问题描述

小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。

思路

  1. 初始化变量

    • m 和 n 分别是两个输入字符串 dna1 和 dna2 的长度。
  2. 创建动态规划表

    • 创建一个 (m+1) x (n+1) 的二维数组 dp,用于存储从 dna1 的前 i 个字符到 dna2 的前 j 个字符的最小编辑距离。
  3. 初始化边界条件

    • dp[i][0] 表示将 dna1 的前 i 个字符转换成空字符串需要的编辑次数,这相当于删除 i 个字符,因此初始化为 i
    • dp[0][j] 表示将空字符串转换成 dna2 的前 j 个字符需要的编辑次数,这相当于插入 j 个字符,因此初始化为 j
  4. 填充动态规划表

    • 使用两层循环遍历 dp 表的每个单元格。

    • 如果 dna1 的第 i 个字符和 dna2 的第 j 个字符相同,则 dp[i][j] 的值等于 dp[i-1][j-1],因为不需要额外的操作。

    • 如果字符不同,则考虑三种操作:

      • 删除操作:dp[i-1][j] + 1,即删除 dna1 的第 i 个字符。
      • 插入操作:dp[i][j-1] + 1,即在 dna1 中插入一个字符以匹配 dna2 的第 j 个字符。
      • 替换操作:dp[i-1][j-1] + 1,即将 dna1 的第 i 个字符替换为 dna2 的第 j 个字符。
    • dp[i][j] 的值是这三种操作中编辑次数最少的一个。

  5. 返回结果

    • 最终,dp[m][n] 存储了将整个 dna1 转换成整个 dna2 所需的最少编辑次数,即两个字符串之间的编辑距离。

代码

def solution(dna1, dna2):
    m = len(dna1)
    n = len(dna2)
    
    # 创建一个(m+1) x (n+1)的二维列表来保存编辑距离
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    # 初始化第一行和第一列
    for i in range(m + 1):
        dp[i][0] = i  # 从dna1到空字符串的操作数为i(删除操作)
    for j in range(n + 1):
        dp[0][j] = j  # 从空字符串到dna2的操作数为j(插入操作)

    # 填充DP表
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if dna1[i - 1] == dna2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]  # 字符匹配,无需操作
            else:
                dp[i][j] = min(dp[i - 1][j] + 1,      # 删除操作
                               dp[i][j - 1] + 1,      # 插入操作
                               dp[i - 1][j - 1] + 1)  # 替换操作

    return dp[m][n]  # 返回最终的编辑距离

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
    print(solution("AGCCGAGC", "GCTAGCT") == 4)

  1. 最小替换子串长度

问题描述

小F得到了一个特殊的字符串,这个字符串只包含字符ASDF,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得ASDF这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。

思路

  1. 初始化变量

    • n 是输入字符串 input 的长度。
    • target 是每个字符 'A', 'S', 'D', 'F' 需要达到的目标频次,即 n // 4
  2. 统计频次

    • 使用字典 freq 来统计字符串中每个字符 'A', 'S', 'D', 'F' 的出现频次。
  3. 检查是否已经平衡

    • 如果 freq 中每个字符的频次都等于 target,则字符串已经平衡,返回 0。
  4. 尝试每个可能的长度

    • 遍历所有可能的子串长度,从 1 到 n
    • 对于每个长度,遍历所有可能的起始位置。
  5. 计算替换子串后的频次

    • 对于每个子串,创建 temp 字典的副本,用于计算替换子串后的频次。
    • 遍历子串中的每个字符,减少 temp 中相应字符的计数。
  6. 检查是否可以通过添加字符达到平衡

    • 计算 temp 中的最大频次 max_count 和最小频次 min_count
    • 如果 max_count - min_count 小于等于子串长度 length,则有可能通过添加字符达到平衡。
  7. 检查是否可以分配字符来达到平衡

    • 计算达到平衡所需的额外字符总数 needed
    • 如果 needed 小于等于子串长度 length,则找到了一个可以替换的子串,返回 length
  8. 返回结果

    • 如果没有找到可以替换的子串,则返回字符串的长度 n,表示需要替换整个字符串。

代码

def solution(input):
    n = len(input)
    target = n // 4  # 目标频次
    
    # 统计频次
    freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}
    for c in input:
        freq[c] += 1
        
    # 已经平衡
    if all(f == target for f in freq.values()):
        return 0
        
    # 尝试每个可能的长度
    for length in range(1, n + 1):
        # 检查每个起始位置
        for start in range(n - length + 1):
            # 计算替换该子串后的频次
            temp = freq.copy()
            for i in range(start, start + length):
                temp[input[i]] -= 1
                
            # 检查是否可以通过添加length个字符达到平衡
            max_count = max(temp.values())
            min_count = min(temp.values())
            
            if max_count - min_count <= length:
                # 检查是否可以分配length个字符来达到平衡
                needed = sum(max(0, target - count) for count in temp.values())
                if needed <= length:
                    return length
                    
    return n

if __name__ == "__main__":
    # 测试用例
    print(solution("ADDF") == 1)  # True
    print(solution("ASAFASAFADDD") == 3)  # True
    print(solution("SSDDFFFFAAAS") == 1)  # True
    print(solution("AAAASSSSDDDDFFFF") == 0)  # True
    print(solution("AAAADDDDAAAASSSS") == 4)  # True

  1. 环状 DNA 序列的最小表示法

问题描述

小C正在研究一种环状的 DNA 结构,它由四种碱基ACGT构成。这种环状结构的特点是可以从任何位置开始读取序列,因此一个长度为 n 的碱基序列可以有 n 种不同的表示方式。小C的任务是从这些表示中找到字典序最小的序列,即该序列的“最小表示”。

例如:碱基序列 ATCA 从不同位置读取可能的表示有 ATCATCAACAATAATC,其中 AATC 是字典序最小的表示。

思路

  1. 初始化变量

    • n 是输入的 DNA 序列 dna_sequence 的长度。
    • min_rotation 初始化为原始序列,这是比较的基准。
  2. 生成所有旋转

    • 通过一个循环,从 1 到 n-1(因为包括原始序列,所以不需要从 0 开始),生成序列的所有可能旋转。
    • 对于每个 i,通过 dna_sequence[i:] + dna_sequence[:i] 生成第 i 次旋转。这个表达式意味着从索引 i 到序列末尾的子串,加上从序列开始到索引 i 的子串。
  3. 比较字典序

    • 对于每个生成的旋转,使用 < 运算符比较其字典序与当前 min_rotation 的字典序。
    • 如果新生成的旋转的字典序小于 min_rotation 的字典序,则更新 min_rotation 为这个新的旋转。
  4. 返回结果

    • 循环结束后,min_rotation 将包含所有旋转中字典序最小的旋转,函数返回这个值。

代码

def solution(dna_sequence):
    n = len(dna_sequence)
    min_rotation = dna_sequence  # 初始化最小旋转为原始序列

    # 生成所有旋转并找出最小的字典序
    for i in range(1, n):
        rotation = dna_sequence[i:] + dna_sequence[:i]  # 生成第 i 次旋转
        if rotation < min_rotation:  # 比较字典序
            min_rotation = rotation  # 更新最小旋转
    
    return min_rotation  # 返回最小表示

def main():
    # 添加测试用例
    print(solution("ATCA") == "AATC")
    print(solution("CGAGTC") == "AGTCCG")
    print(solution("TCATGGAGTGCTCCTGGAGGCTGAGTCCATCTCCAGTAG") == "AGGCTGAGTCCATCTCCAGTAGTCATGGAGTGCTCCTGG")

main()
  1. 优化青海湖至景点X的租车路线成本

问题描述

小F计划从青海湖出发,前往一个遥远的景点X进行旅游。景点X可能是“敦煌”或“月牙泉”,线路的路径是唯一的。由于油价的不断上涨,小F希望尽量减少行程中的燃油成本。车辆的油箱容量为400L,在起始点租车时,车内剩余油量为 200L。每行驶 1km 消耗 1L 油。沿途设有多个加油站,小F可以在这些加油站补充燃油;此外,到达目标景点X还车的时候,需要保证车内剩余的油至少有 200L。

小F需要你帮助他计算,如果合理规划加油站的加油顺序和数量,最小化从青海湖到景点X的旅行成本(元)。

思路

  1. 初始化变量

    • maxCapacity 是汽车油箱的最大容量。
    • inf 是一个很大的数,用于初始化动态规划(DP)数组中的值,表示无穷大。
  2. 排序和计算距离

    • 将加油站按照位置升序排序。
    • 计算每个加油站之间的距离,并存储在 dis 数组中。
  3. 初始化 DP 数组

    • 创建一个 DP 数组 dp,其大小为 (n+2) x (maxCapacity+1),其中 n 是加油站的数量。dp[i][j] 表示到达第 i 个加油站时,剩余油量为 j 时的最小油量消耗。
  4. 动态规划计算最小花费

    • 使用三层循环填充 DP 数组:

      • 外层循环遍历每个加油站。
      • 中层循环遍历可能的剩余油量。
      • 内层循环遍历可能的油量消耗。
    • 对于每个加油站,计算从上一个加油站到达当前加油站的最小油量消耗,考虑到加油站提供的油量和两个加油站之间的距离。

  5. 判断是否可以到达终点

    • 计算到达终点后剩余的油量 remaining_fuel
    • 如果 remaining_fuel 超出油箱容量或者为负数,返回 -1,表示无法到达终点。
  6. 计算结果

    • 在 DP 数组的最后一行中找到最小的油量消耗,这是到达终点的最小油量消耗。
  7. 返回结果

    • 如果结果仍然是 inf,表示无法到达终点,返回 -1
    • 否则,返回计算出的最小油量消耗。

代码

import sys

def solution(distance, n, gasStations):
    maxCapacity = 400
    inf = sys.maxsize

    # 按照加油站位置升序排序
    gasStations.sort(key=lambda x: x[0])

    # 计算每个加油站之间的距离
    dis = [gasStations[0][0]]
    for i in range(1, len(gasStations)):
        dis.append(gasStations[i][0] - gasStations[i-1][0])

    # 初始化 dp 数组
    dp = [[inf] * (maxCapacity + 1) for _ in range(n + 2)]
    dp[0][200] = 0  # 初始状态,容量为200,花费为0

    # 动态规划计算最小花费
    for i in range(1, n + 1):
        for j in range(maxCapacity + 1):
            for k in range(maxCapacity + 1):
                if j + dis[i-1] - k >= 0 and k >= dis[i-1]:
                    dp[i][j] = min(dp[i][j], dp[i-1][k] + (j + dis[i-1] - k) * gasStations[i-1][1])

    # 判断是否可以到达终点
    remaining_fuel = 200 + distance - gasStations[n-1][0]

    if remaining_fuel > maxCapacity or remaining_fuel < 0:
        return -1
    
    result = inf
    for i in range(remaining_fuel, maxCapacity + 1):
        result = min(result, dp[n][i])

    if result == inf:
        return -1
    
    return result

if __name__ == "__main__":
    # 测试样例
    gas_stations1 = [[100, 1], [200, 30], [400, 40], [300, 20]]
    gas_stations2 = [[300, 25], [600, 35], [900, 5]]
    gas_stations3 = [[100, 50], [150, 45]]
    gas_stations4 = [[100, 10], [200, 20], [300, 30], [400, 40], [600, 15]]
    gas_stations5 = [[25, 100]]

    # 测试用例
    print(solution(500, 4, gas_stations1) == 4300)
    print(solution(1000, 3, gas_stations2) == -1)
    print(solution(200, 2, gas_stations3) == 9000)
    print(solution(700, 5, gas_stations4) == 9500)
    print(solution(50, 1, gas_stations5) == 5000)