题目解析 Day 4 | 豆包MarsCode AI刷题

251 阅读19分钟

1. 找出最长的神奇数列

问题描述

小F是一个好学的中学生, 今天他学习了数列的概念。他在纸上写下了一个由01组成的正整数序列,长度为n。这个序列中的10交替出现,且至少由 3 个连续的01组成的部分数列称为「神奇数列」。例如,10101是一个神奇数列,而1011不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪个。你能帮他找到吗?

如果有多个神奇数列,那么输出最先出现的一个。

解题思路

我们可以使用暴力算法,通过遍历所有可能的子串,判断其是否符合神奇数列的条件,时间复杂度是O(n2)O(n^2)。我们还可以使用动态规划算法,首先定义状态,dp[i] 表示以 inp[i] 结尾的最长神奇数列的长度。然后写出状态转移方程:如果 inp[i] == inp[i - 1],则 dp[i] = 1,因为当前字符与前一个字符相同,无法形成交替序列。如果 inp[i] != inp[i - 1],则 dp[i] = dp[i - 1] + 1,因为当前字符与前一个字符不同,可以形成更长的交替序列。

代码实现

def solution(inp): # 动态规划算法
    n = len(inp)
    if n < 3:
        return ""
    
    # 初始化dp数组
    dp = [1] * n
    max_length = 0
    max_sequence = ""
    start = 0
    
    # 动态规划计算最长神奇数列
    for i in range(1, n):
        if inp[i] != inp[i - 1]:
            dp[i] = dp[i - 1] + 1
        else:
            dp[i] = 1
        
        # 如果当前长度大于最大长度,更新最长神奇数列
        if dp[i] > max_length:
            max_length = dp[i]
            start = i - max_length + 1
    
    # 如果最长的神奇数列长度小于3,返回空字符串
    if max_length < 3:
        return ""
    
    return inp[start:start + max_length]

def solution(inp): # 暴力算法
    max_length = 0
    max_sequence = ""
    
    # 遍历字符串,检查每个可能的子序列
    for i in range(len(inp)):
        for j in range(i + 3, len(inp) + 1):
            subseq = inp[i:j]
            # 检查子序列是否符合神奇数列的条件
            if is_magical(subseq):
                # 如果找到更长的神奇数列,更新记录
                if len(subseq) > max_length:
                    max_length = len(subseq)
                    max_sequence = subseq
    
    return max_sequence

# 辅助函数:检查子序列是否符合神奇数列的条件
def is_magical(subseq):
    # 检查子序列是否由0和1交替组成
    for k in range(1, len(subseq)):
        if subseq[k] == subseq[k - 1]:
            return False
    return True

2. 字符串最短循环子串

问题描述

小M在研究字符串时发现了一个有趣的现象:某些字符串是由一个较短的子串反复拼接而成的。如果能够找到这个最短的子串,便可以很好地还原字符串的结构。你的任务是给定一个字符串,判断它是否是由某个子串反复拼接而成的。如果是,输出该最短的子串;否则,输出空字符串""。例如:当输入字符串为abababab时,它可以由子串ab反复拼接而成,因此输出ab;而如果输入ab,则该字符串不能通过子串的重复拼接得到,因此输出空字符串。

解题思路

我们可以遍历所有可能的子串长度,从1到字符串长度的一半。对于每个子串长度,检查是否可以通过重复该子串来构造原字符串。如果找到这样的子串,返回该子串;否则,返回空字符串。

代码实现

def solution(inp):
    # 获取字符串的长度
    n = len(inp)
    
    # 遍历所有可能的子串长度
    for length in range(1, n // 2 + 1):
        # 检查当前长度的子串是否可以重复构成原字符串
        if n % length == 0:
            # 获取当前长度的子串
            substring = inp[:length]
            # 重复子串并检查是否等于原字符串
            if substring * (n // length) == inp:
                return substring
    
    # 如果没有找到符合条件的子串,返回空字符串
    return ""

3. 优秀项目组初选评比

问题描述

公司正在进行优秀项目组评比的初选工作,参赛者有小C、小U、小R、小S、小M和小F的项目组。评委会已经根据他们提交的材料完成了打分,各个项目组的得分分别是s1,s2,s3,,sks_1,s_2,s_3,\ldots,s_k。评委们希望设定一个初选晋级的分数线xx,让所有得分大于xx的项目组晋级,其他的项目组将被淘汰。此外,评委们希望晋级的项目组数量和淘汰的项目组数量都在区间[m,n][m,n]之间。

显然,这个分数线xx可能不存在,也可能存在多个满足条件的分数线。如果不存在满足条件的xx,则输出1-1;如果存在多个满足条件的分数线xx,则输出满足条件的最小分数线。

解题思路

首先,我们对得分列表进行排序,这样当存在多个满足条件的分数线xx,我们更容易输出满足条件的最小分数线。然后,我们遍历所有可能的分数线,检查每个分数对应的晋级和淘汰的项目组数量是否满足条件,如果满足,则直接返回该分数。这题还可以用变体的二分查找。

代码实现

def solution(m: int, n: int, a: list) -> int:
    # 1. 对得分列表进行排序
    a.sort()
    
    # 2. 遍历所有可能的分数线
    for i in range(len(a)):
        # 计算当前分数线 a[i] 对应的晋级和淘汰的项目组数量
        晋级项目组数量 = len(a) - i - 1
        淘汰项目组数量 = i + 1
        
        # 3. 检查条件
        if m <= 晋级项目组数量 <= n and m <= 淘汰项目组数量 <= n:
            # 4. 返回结果
            return a[i]
    
    # 5. 如果没有找到满足条件的分数线,返回 -1
    return -1

4. 小 C 点菜问题

问题描述

小C来到了一家餐馆,准备点一些菜。

已知该餐馆有 nn 道菜,第 ii 道菜的售价为 wiw_i

小C准备点一些价格相同的菜,但小C不会点单价超过 mm 的菜。

小C想知道,自己最多可以点多少道菜?

解题思路

首先,我们过滤掉那些价格超过 m 的菜品,因为小C不会点这些菜。接下来,我们使用字典统计每种价格的菜品数量。最后,我们找出数量最多的菜品,这个数量就是小C最多可以点的菜的数量。

代码实现

def solution(m: int, w: list) -> int:
    # 过滤价格不超过 m 的菜品
    valid_prices = [price for price in w if price <= m]
    
    # 统计每种价格的菜品数量
    price_count = {}
    for price in valid_prices:
        if price in price_count:
            price_count[price] += 1
        else:
            price_count[price] = 1
    
    # 找出数量最多的菜品
    max_count = 0
    for count in price_count.values():
        if count > max_count:
            max_count = count
    
    return max_count

5. 小 C 的外卖超时判断

问题描述

小C点了一个外卖,并且急切地等待着骑手的送达。她想知道她的外卖是否超时了。

已知小C在时刻 t1 点了外卖,外卖平台上显示的预计送达时间为 t2,而实际送达时间为 t3。需要判断外卖是否超时。如果外卖超时,则输出 "Yes";否则输出 "No"

t3 在 t2 之后则认定为超时。

实际送达时间与预计送达时间在 2 小时之内。

解题思路

首先,我们解析时间字符串,将时间字符串转换为分钟数,便于比较。然后计算t2 和 t3距离t1的分钟数。我们需要额外处理跨天时间,如果 t 在 t1 之前,说明 t 是第二天的时间。最后,我们比较时间,判断 t3 是否在 t2 之后。

代码实现

def solution(t1: str, t2: str, t3: str) -> str:
    # 将时间字符串转换为分钟数
    def time_to_minutes(t: str) -> int:
        hours, minutes = map(int, t.split(':'))
        return hours * 60 + minutes
    
    # 将 t1 转换为分钟数
    t1_minutes = time_to_minutes(t1)
    
    # 将 t2 和 t3 转换为相对于 t1 的分钟数
    def relative_minutes(t: str, base: int) -> int:
        t_minutes = time_to_minutes(t)
        if t_minutes < base:
            return t_minutes + 24 * 60 - base  # 跨天处理
        return t_minutes - base
    
    t2_relative = relative_minutes(t2, t1_minutes)
    t3_relative = relative_minutes(t3, t1_minutes)
    
    # 判断 t3 是否在 t2 之后
    if t3_relative > t2_relative:
        return "Yes"
    else:
        return "No"

6. 小 C 的排列询问

问题描述

小C拿到了一个排列,她想知道在这个排列中,元素 x 和 y 是否是相邻的。排列是一个长度为 n 的数组,其中每个数字从 1 到 n 恰好出现一次。

你的任务是判断在给定的排列中,x 和 y 是否是相邻的。

解题思路

首先,我们遍历一次数组,找到数字x的下标。然后,我们判断与x相邻的数字是否是y。如果是,返回True,否则返回False。

代码实现

def solution(n: int, a: list, x: int, y: int) -> bool:
    # 找到 x 的位置
    x_index = -1
    for i in range(n):
        if a[i] == x:
            x_index = i
            break
    
    # 如果 x 不在数组中,返回 False
    if x_index == -1:
        return False
    
    # 检查 y 是否与 x 相邻
    if x_index > 0 and a[x_index - 1] == y:
        return True
    if x_index < n - 1 and a[x_index + 1] == y:
        return True
    
    # 如果没有找到相邻的 y,返回 False
    return False

7. 小 C 的类二进制拼图

问题描述

小C发现了一种特殊的数字,称为类二进制数字,即仅由数字0和1组成的十进制数。例如,1011100都是类二进制数字,而1123001则不是。现在,小C手上有一个正整数n,他想知道最少需要多少个类二进制数字相加才能得到n

解题思路

需要最少得类二进制数字的个数取决于正整数n数值最大的那一位。因此,我们遍历字符串,找到数值最大的那位数字就是答案。或者我们可以使用分解方法:为了使类二进制数字相加得到 n,可以考虑把 n 按照每个位上的 1 分解。例如,假设 n = 231,其分解过程为:231 = 111 + 110 + 10。每一步都使用了最大的类二进制数以减少相加次数。贪心策略:对 n 的每一位,尽量找到该位数上的最大类二进制数,这样就可以用较少的类二进制数分解 n

代码实现

def solution(n: str) -> int:
    max_digit = 0  # 用于记录最高位的数字
    
    # 遍历数字的每一位
    for digit in n:
        digit = int(digit)  # 将字符转换为整数
        
        # 更新最高位的数字
        if digit > max_digit:
            max_digit = digit
    
    return max_digit
  
def solution(n: str) -> int:
    count = 0
    n = int(n)
    while n > 0:
        max_binary_like = int(''.join(['1' if digit != '0' else '0' for digit in str(n)]))
        n -= max_binary_like
        count += 1
    return count

8. 小 M 的奶酪问题

问题描述

小M在集市上买了一公斤奶酪回家。然而,在小M不在的时候,小F偷偷地偷走了 AB\frac{A}{B} 公斤的奶酪。现在,小M想知道他还剩下多少奶酪。要求答案以分数的形式表示,并且分数的分母必须为 BB

解题思路

我们直接使用 B - A 来计算剩余的奶酪量的分子,因为原本有1公斤奶酪,偷走了 A/B 公斤,剩下的奶酪量就是 1 - A/B,可以简化为 (B - A) / B

代码实现

def solution(A: int, B: int) -> str:
    # 计算剩余的奶酪量
    remaining_cheese = B - A
    
    # 将剩余的奶酪量表示为分数形式,分母为 B
    return f"{remaining_cheese}/{B}"

9. 小 T 的密码变换规则

问题描述

小T设计了一套密码变换规则,将输入的字符串转换成一串数字密码。变换规则如下:

  1. 小写字母的映射

    • a, b, c -> 2
    • d, e, f -> 3
    • g, h, i -> 4
    • j, k, l -> 5
    • m, n, o -> 6
    • p, q, r, s -> 7
    • t, u, v -> 8
    • w, x, y, z -> 9
  2. 大写字母的处理

    • 大写字母先转为小写字母,再跳到字母表中的前一个字母,并按上述规则转换为对应的数字。
    • 例如,B 转换为 a,再转换为 2A 特殊处理,先变为 Z,再转换为 9
  3. 非字母字符的处理

    • 非字母字符保持不变。

解题思路

首先,我们创建一个字典来存储小写字母到数字的映射关系。对于大写字母,我们需要先将其转换为小写字母,然后跳到字母表中的前一个字母,再进行映射。非字母字符保持不变。最后,我们遍历输入字符串的每个字符,根据其类型进行相应的转换。

代码实现

def solution(s: str) -> str:
    # 创建字母到数字的映射字典
    letter_to_digit = {
        'a': '2', 'b': '2', 'c': '2',
        'd': '3', 'e': '3', 'f': '3',
        'g': '4', 'h': '4', 'i': '4',
        'j': '5', 'k': '5', 'l': '5',
        'm': '6', 'n': '6', 'o': '6',
        'p': '7', 'q': '7', 'r': '7', 's': '7',
        't': '8', 'u': '8', 'v': '8',
        'w': '9', 'x': '9', 'y': '9', 'z': '9'
    }
    
    # 初始化结果字符串
    result = []
    
    # 遍历输入字符串的每个字符
    for char in s:
        if char.islower():
            # 如果是小写字母,直接映射
            result.append(letter_to_digit[char])
        elif char.isupper():
            # 如果是大写字母,先转换为小写字母,再跳到前一个字母
            if char == 'A':
                # 特殊处理 'A'
                result.append(letter_to_digit['z'])
            else:
                # 其他大写字母
                prev_char = chr(ord(char.lower()) - 1)
                result.append(letter_to_digit[prev_char])
        else:
            # 非字母字符保持不变
            result.append(char)
    
    # 将结果列表转换为字符串并返回
    return ''.join(result)

10. 数值操作的期望计算问题

问题描述

小R有两个正整数 a 和 b,她将随机选择其中一个数并将其乘以 2。这个操作会进行两次,操作后的两个数之和会有所变化。小R想知道,操作结束后,两个数之和的期望值是多少?

例如:如果 a=3a=3b=3b=3,那么有 1/21/2 的概率两数之和为 12121/21/2 的概率两数之和为 1515。因此,期望值为 13.513.5

解题思路

  1. 期望值计算

    • 每次操作有 1/2 的概率选择 a1/2 的概率选择 b

    • 操作两次后,可能的结果有:

      • a 被乘以 2 两次,结果为 4a + b
      • a 被乘以 2 一次,b 被乘以 2 一次,结果为 2a + 2b
      • b 被乘以 2 一次,a 被乘以 2 一次,结果为 2a + 2b
      • b 被乘以 2 两次,结果为 a + 4b
    • 每种结果的概率都是 1/4

  2. 期望值公式

    • 期望值 E 可以表示为: E=14×(4a+b)+12×(2a+2b)+14×(a+4b)E = \frac{1}{4} \times (4a + b) + \frac{1}{2} \times (2a + 2b) + \frac{1}{4} \times (a + 4b)
    • 简化后: E=94×(a+b) E = \frac{9}{4} \times (a + b)

代码实现

def solution(a: int, b: int) -> str:
    # 计算期望值
    expected_value = (9 / 4) * (a + b)
    
    # 将结果格式化为字符串,保留两位小数
    result = f"{expected_value:.2f}"
    
    return result

11. 数组重排最小化差值

问题描述

小C 和小U 有两个数组,分别是 a 和 b,它们的长度相同。小U 想通过重新排列数组 a 的元素,来最小化 a 和 b 之间的差异。具体来说,他们要最小化所有元素差值绝对值之和,即 sum(abs(a[i] - b[i]))。 你能帮助小C 和小U 找到这个最小化的值吗?

解题思路

首先,我们可以对数组 a 和 b 进行排序。然后,我们可以将排序后的 a 和 b 进行匹配,使得每个 a[i] 尽量接近 b[i]。最后,计算所有元素差值的绝对值之和。留个小问题,如何严谨的证明这种排列差异最小。

代码实现

def solution(a: list, b: list) -> int:
    # 1. 对数组 a 和 b 进行排序
    a.sort()
    b.sort()
    
    # 2. 初始化差异总和
    total_diff = 0
    
    # 3. 计算每个元素的差异
    for i in range(len(a)):
        total_diff += abs(a[i] - b[i])
    
    # 4. 返回差异总和
    return total_diff

12. 选择题反选效果分析

问题描述

小U正在检查某同学的选择题答案。试卷共有 n 道题目,每道题目只有两个选项 A 和 B。当前小U手上有两组答案:

  1. s:该同学的原始答案。
  2. t:标准答案。

小U想知道,如果将该同学的所有答案都反选(即:如果某题的答案是 A 则改成 B,如果是 B 则改成 A),那么在反选之后,正确的答案数量是否会增加?具体结果有三种可能:

  1. 如果反选后的正确答案数 增加,输出 "yes"。
  2. 如果反选后的正确答案数 不变,输出 "draw"。
  3. 如果反选后的正确答案数 减少,输出 "no"。

解题思路

首先,我们遍历 s 和 t,计算原始答案 s 中有多少题是正确的。然后,我们遍历 s 和 t,计算反选后的答案 s' 中有多少题是正确的。最后,我们比较这两个正确答案的数量,输出相应的结果。

代码实现

def solution(n: int, s: str, t: str) -> str:
    # 计算原始答案 s 中有多少题是正确的
    original_correct = 0
    for i in range(n):
        if s[i] == t[i]:
            original_correct += 1
    
    # 计算反选后的答案 s' 中有多少题是正确的
    flipped_correct = 0
    for i in range(n):
        if s[i] != t[i]:
            flipped_correct += 1
    
    # 比较原始正确答案数和反选后的正确答案数
    if flipped_correct > original_correct:
        return "yes"
    elif flipped_correct == original_correct:
        return "draw"
    else:
        return "no"

13. 二进制反码转换问题

问题描述

小C在学习二进制运算,他了解到每个非负整数都有其二进制表示。例如,整数 5 可以被表示为二进制 "101",整数 11 可以被表示为二进制 "1011",并且除了 N = 0 外,任何二进制表示中都不含前导零。

二进制的反码表示是将每个 1 变为 0,每个 0 变为 1。例如,二进制数 "101" 的二进制反码为 "010"。现在小C想知道,给定一个十进制数 N,它的二进制反码对应的十进制数是多少。

解题思路

首先,我们使用 bin() 函数将十进制数转换为二进制字符串,并去掉前缀 '0b'。然后,我们遍历二进制字符串,将每个字符进行反码操作。最后,我们使用 int() 函数将二进制字符串转换回十进制数。

代码实现

def solution(N: int) -> int:
    # 将十进制数转换为二进制字符串,并去掉前缀 '0b'
    binary_str = bin(N)[2:]
    
    # 对二进制字符串进行反码操作
    inverted_binary_str = ''.join('1' if bit == '0' else '0' for bit in binary_str)
    
    # 将反码后的二进制字符串转换回十进制数
    result = int(inverted_binary_str, 2)
    
    return result

14. 充电总时间计算

问题描述

小R有n部电脑,每部电脑的电池容量分别为aia_i。她可以使用两种不同的充电方式来给电脑充电:

  1. 普通充电:每单位时间为电脑充电xx单位的电量。
  2. 闪充:每单位时间为电脑充电4x4x单位的电量。

现在,所有电脑的电量都为零。小R希望使用闪充给所有电脑充满电,计算她需要的总充电时间。请保留结果的小数点后两位。

解题思路

首先,我们计算每部电脑充满电所需的时间。使用闪充时,每单位时间充电 4x 单位。因此,每部电脑充满电所需的时间为 ai/(4x)a_i / (4x)。然后,我们将所有电脑充满电所需的时间求和,得到总时间。最后,我们格式化输出,将总时间保留两位小数,并转换为字符串格式。

代码实现

def solution(n: int, x: int, a: list) -> str:
    # 初始化总时间为0
    total_time = 0.0
    
    # 遍历每部电脑的电池容量
    for battery in a:
        # 计算每部电脑充满电所需的时间,并累加到总时间
        total_time += battery / (4 * x)
    
    # 将总时间保留两位小数,并转换为字符串格式
    result = f"{total_time:.2f}"
    
    return result

15. 判断回旋镖的存在

问题描述

小M正在玩一个几何游戏,给定一个二维平面上的三个点 points,其中每个点用坐标 points[i] = [xi, yi] 表示。如果三点构成一个回旋镖,则返回 true。回旋镖的定义是三点不在一条直线上,并且这三个点互不相同。

请你帮助小M判断这些点是否构成一个回旋镖。

解题思路

首先,我们通过比较每个点的坐标来判断是否相同。然后,我们通过计算斜率来判断点是否共线。如果三个点的斜率相同,则它们在一条直线上。

代码实现

def solution(points: list) -> bool:
    # 检查点是否相同
    if points[0] == points[1] or points[1] == points[2] or points[0] == points[2]:
        return False
    # return not (((points[1][1] - points[0][1]) * (points[2][0] - points[1][0])) == ((points[2][1] - points[1][1]) * (points[1][0] - points[0][0])))
    # 计算斜率
    # 计算点1和点2的斜率
    slope1 = (points[1][1] - points[0][1]) / (points[1][0] - points[0][0]) if points[1][0] != points[0][0] else float('inf')
    # 计算点2和点3的斜率
    slope2 = (points[2][1] - points[1][1]) / (points[2][0] - points[1][0]) if points[2][0] != points[1][0] else float('inf')
    
    # 如果斜率相同,则三点共线
    if slope1 == slope2:
        return False
    
    return True

16. 判断数组是否单调

问题描述

小S最近在研究一些数组的性质,她发现有一种非常有趣的数组被称为 单调数组。如果一个数组是单调递增或单调递减的,那么它就是单调的。

  • 当对于所有索引i <= j时,nums[i] <= nums[j],数组nums是单调递增的。
  • 当对于所有索引i <= j时,nums[i] >= nums[j],数组nums是单调递减的。

你需要编写一个程序来判断给定的数组nums是否为单调数组。如果是,返回true,否则返回false

解题思路

首先,我们定义两个布尔变量来分别表示数组是否是单调递增或单调递减。然后,我们遍历数组中的每一对相邻元素,检查它们的关系。如果某对元素破坏了递增或递减性质,那么,我们就根据相邻元素的关系更新布尔变量。最后,我们返回结果,如果两个布尔变量中有一个为 True,则数组是单调的。

代码实现

def solution(nums: list) -> bool:
    # 初始化标志
    is_increasing = True
    is_decreasing = True
    
    # 遍历数组
    for i in range(len(nums) - 1):
        # 检查相邻元素的关系
        if nums[i] > nums[i + 1]:
            is_increasing = False
        if nums[i] < nums[i + 1]:
            is_decreasing = False
    
    # 返回结果
    return is_increasing or is_decreasing

今天的AI刷题题目解析就到这里,小伙伴们如果有更好的解题思路,欢迎在评论区留言一起交流。