【笔试分享】3月22日淘天暑期实习算法岗解析

关注我:二仙桥耐笔王 , 强力1v1辅导暑期实习&春招笔试

第1题-游游的字符串

题目内容

游游正在进行字符串对照试验,他有一个长度为 的字符串和另一个长度同样为的字符串 ,他先定义一个字符:可控一级,当其为小写字母;可控二级,当其为大写字母;可控三级,当其为数字;不可控,当其为其他字符。随后,他将依次对每一个进行以下操作:

  • 如果的第个字符同时为可控的,且等级相同,则输出这两个字符的中位 码对应的字符;
  • 如果的第个字符同时为可控的,但等级不同,则输出这两个字符的中位 码;
  • 否则,直接输出一条下划线"_"。

在这里,记字符 码为,则它们的 中位码定义为,的平 均值向上取整。例如,的中位 码为;的中位 码为

输入描述

第一行输入一个整数代表字符串的长度。

第二行输入一个长度为 的字符串

第三行输入一个长度为n 的字符串

除此之外,保证字符串由数字、大小写字母、空格及这七个常见半角符号混合构成。保证 字符串的首尾不为空格。

输出描述

在一行上输出一个字符串,代表操作过后的字符串。。

样例1

输入

9
ciaLlo!?
dAmE*+-/

输出

8485g|____

说明

对于第一个字符,两者同时为可控的,但等级分别为二级和一级,所以,直接输出中位码, 查表可得,的平均值向上取整为

对于第二个字符,两者同时为可控的,但等级分别为一级和二级,所以,直接输出中位Ascii码, 查表可得,的平均值向上取整为

对于第三个字符,两者同时为一级可控,所以,输出中位码对应的字符,查表可得, 的平均值向上取整为,对应

题解思路

这道题目要求对两个字符串 st 进行比较,根据每个字符的类型和等级输出对应的结果。具体操作如下:

步骤分析

  1. 字符分类与等级判断
    每个字符有不同的等级,可以分为四类:

    • 可控一级:小写字母 a-z,其 ASCII 值在 97-122 之间。
    • 可控二级:大写字母 A-Z,其 ASCII 值在 65-90 之间。
    • 可控三级:数字 0-9,其 ASCII 值在 48-57 之间。
    • 不可控字符:其他符号或字符,如空格、标点符号等。

    对每个字符,我们需要判断其属于哪种类型,如果两个字符同时为可控字符,接下来的操作才有意义。

  2. 等级比较与输出

    • 如果 s[i]t[i] 都是可控字符,且它们的等级相同,则输出它们 ASCII 值的中位字符(向上取整的平均值)。
    • 如果 s[i]t[i] 都是可控字符,但它们的等级不同,则输出它们 ASCII 值的中位数(向上取整的平均值)。
    • 如果 s[i]t[i] 不是可控字符,则直接输出下划线 _
  3. 计算中位 ASCII 值
    中位 ASCII 值定义为两个字符 ASCII 值的平均值向上取整,可以通过 (a + b + 1) // 2 来实现,其中 ab 是字符的 ASCII 值。

代码实现

def check(c):
    # 判断字符类型
    if c.islower():
        return 1
    elif c.isupper():
        return 2
    elif c.isdigit():
        return 3
    return 0

def solve(n, s, t):
    result = []
    for i in range(n):
        if check(s[i]) > 0 and check(t[i]) > 0:
            # 获取字符的ASCII值
            a = ord(s[i])
            b = ord(t[i])
            # 确保a <= b
            if a > b:
                a, b = b, a
            # 如果等级相同,输出字符,否则输出ASCII码
            if check(s[i]) == check(t[i]):
                mid_char = chr((a + b + 1) // 2)
                result.append(mid_char)
            else:
                mid_ascii = (a + b + 1) // 2
                result.append(str(mid_ascii))
        else:
            result.append('_')
    return ''.join(result)

# 输入
n = int(input())
s = input()
t = input()

# 输出结果
print(solve(n, s, t))

第2题-二进制数组

题目内容

对于给定的正偶数和正整数,求解下式:

这显然难不倒你,所以,我们将会使用一种特殊的方式给出的二进制形式:给出一个由个整数构成的数组{},其中,第个整数代表的二进制表示中,从高位到低位,恰好有连续。更具体地,如果数组={},那么,第一个整数就代表有,第二个整数就代表有。最终,可以得到的二进制表示为

输入描述

第一行输入两个整数

第二行输入个正整数

除此之外,保证单个测试文件的之和不超过

输出描述

输出一个十进制整数,代表式子的最终答案.

样例1

输入

4 15
3 4 1 2

输出

12

算法思路

核心观察

  1. 异或特性n XOR (n >> 1) 的二进制中,只有相邻位不同的位置会产生1
  2. 段边界规则:相邻段的奇偶性不同时,交界处会生成1
  3. 快速幂优化:直接计算大数不可行,需用快速幂逐位处理

关键步骤

  1. 前缀和预处理:快速定位每个段的边界位置
  2. 最高位处理:二进制最高位总是由第一个段决定,直接计算贡献
  3. 段边界筛选:只处理奇偶性变化的段边界
  4. 模运算优化:使用快速幂算法处理大指数取模

代码实现

k, m = map(int, input().split())  # 输入k和m
a = list(map(int, input().split()))  # 输入数组a

b = []  # 用来存储二进制序列
x = 0  # 用来暂时存储1的累加和
for i in range(k):
    if a[i] % 2 == 0:  # 偶数表示0
        b.append(x)
        x = 0
        b.append(a[i])  # 偶数部分直接加入
    else:  # 奇数表示1
        x += a[i]  # 累加1的个数
if x != 0:
    b.append(x)  # 最后如果有剩余的1,需要加入b

L = sum(b)  # 计算所有1和0的总个数
pre_sum = 0  # 前缀和
result = 0  # 最终结果

# 遍历b数组进行计算
for i in range(1, len(b) + 1):
    exponent = L - 1 - pre_sum  # 指数部分
    contribution = pow(2, exponent, m)  # 计算贡献值,模m
    result = (result + contribution) % m  # 累加结果
    pre_sum += b[i - 1]  # 更新前缀和

print(result % m)  # 输出最终结果

第3题-最小权值之和

题目内容

小红拿到一棵树,结点总数为,根节点为

定义每个点的权值为到结点的边数。

现在小红可以选择一个非1号结点,使得以该结点为根节点的子树成为号结点的子结点。

求整棵树的最小权值之和是多少?

输入描述

第一行一个整数,表示树的结点总数。

接下来行,每行两个整数,表示之间有一条边。

输出描述

一个整数,表示整棵树的最小权值之和。

样例1

输入

5
1 2
2 3
3 4
4 5

输出

6

题解

思路

本题的思路是:先利用深度优先搜索求出每个结点到根结点的距离(即深度)和每个结点的子树大小,然后计算初始的权值和 ;对于每个非根结点 ,将以 为根的子树挂接到 号结点后,该子树所有结点的深度都会降低 ,所以改善量为 ,最后取所有非根结点中最大的改善量,从 中减去即可得到最小的权值和。

  1. 原始权值和的计算:
    初始时,每个结点的权值等于其到根结点 的距离。可以通过深度优先搜索()计算出所有结点的深度,累加即可得到原始权值和

  2. 操作对权值和的影响分析:
    设选择了结点 ),原来 的深度为 ,且以 为根的子树中的结点数为

    • 操作后, 直接挂接到 下,新深度变为 ;而 子树内任意结点 的深度变为
    • 因此,这部分结点权值减少了
    • 整个子树的权值和减少量为
      =
  3. 目标转换:
    为了使整体权值和最小,我们需要最大化提升量 。因此,最终答案为
    = - { * }.

代码实现

def main():
    import sys
    sys.setrecursionlimit(300000)  # 增加递归深度限制
    n = int(sys.stdin.readline())
    tree = [[] for _ in range(n+1)]  # 构造邻接表
    for _ in range(n-1):
        u, v = map(int, sys.stdin.readline().split())
        tree[u].append(v)
        tree[v].append(u)
    
    depth = [0]*(n+1)         # 存储每个结点的深度
    subtree_size = [0]*(n+1)  # 存储每个结点的子树大小
    total_depth = 0         # 累加所有结点深度,表示 S
    best = 0                # 记录最大提升量

    # 深度优先搜索函数
    def dfs(u, parent, d):
        nonlocal total_depth, best
        depth[u] = d  # 记录结点 $u$ 的深度
        total_depth += d  # 累加到总权值和
        subtree_size[u] = 1  # 初始化子树大小
        for v in tree[u]:
            if v == parent:
                continue  # 避免回到父结点
            dfs(v, u, d+1)  # 对子结点递归
            subtree_size[u] += subtree_size[v]  # 累加子树大小
        if u != 1:  # 只考虑非根结点
            improvement = subtree_size[u] * (d - 1)  # 计算提升量
            best = max(best, improvement)  # 更新最大提升量

    dfs(1, 0, 0)  # 从结点 1 开始深搜,初始深度为 0
    print(total_depth - best)  # 输出结果

if __name__ == '__main__':
    main()

#笔试#
全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客企业服务