【2025春招真题改编】03.09-阿里云春招-开发岗

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题(建议用时:90分钟/套)
  • 对照解析查漏补缺
  • 配套练习题库

互联网必备刷题宝典🔗

题目一:收藏家的序列价值

1️⃣:识别排列子序列的核心性质——必须包含从 1 到长度 k 的连续整数

2️⃣:构建动态规划解法,递推形成长度为 k 的排列子序列数量

3️⃣:通过加权求和计算最终价值总和

整体难度:中等

这道题目要求统计一个数组中满足特定条件的所有子序列价值之和。关键观察是:有价值的子序列必须严格递增且恰好包含 1 到某个 k 的所有整数。通过动态规划,定义 表示能形成序列 的方案数,在遇到元素 1 时初始化子序列,遇到 k>1 时将其接在已有的 子序列后面。最终价值等于每种长度 k 的子序列数量乘以 k 的总和。时间和空间复杂度均为

题目二:优雅字符串的探索

1️⃣:利用去重后字典序递增的关键特性定义优雅字符串

2️⃣:逆序遍历字符串,处理每个可能的子串左端点

3️⃣:维护字符的最新出现位置,从字典序最大的字符开始检查确保递增性

整体难度:中等

本题引入了"优雅字符串"的新概念:去重后字符按原顺序排列时字典序递增。关键挑战在于快速判断从每个位置开始的所有子串中哪些满足优雅条件。采用逆序遍历的巧妙策略,对每个位置作为左端点,通过维护字符最后出现位置和从大到小检查字符字典序,可以确定能够形成优雅子串的最远右边界。算法时间复杂度为 ,空间复杂度为 ,非常适合处理长度不超过 的字符串。

题目三:艺术展览的最优摆放

1️⃣:分解交换操作带来的得分增益,得到简化公式

2️⃣:利用 值范围有限的特性进行分组优化

3️⃣:根据 大小关系选择最优的 值组合

整体难度:中等偏难

这道题目探讨了如何通过最多一次交换操作最大化艺术展览的评分。通过数学推导,发现交换元素 带来的评分变化可简化为 。基于此洞察,不需要考虑所有可能的交换对,而是可以按 值分组记录 的最大值和最小值,然后枚举所有可能的 组合。这种方法将时间复杂度从朴素的 优化到 ,其中 的最大值 1000,非常适合处理数据规模达到 的情况。

01. 收藏家的序列价值

题目内容

小基是一位收藏家,非常喜欢收集各种序列。他特别关注序列的"价值",并制定了一套独特的价值评估体系:

  • 如果一个序列不是严格递增的,它的价值为
  • 如果一个序列是严格递增的,并且恰好包含从 到序列长度的所有整数(即形成一个排列),那么它的价值就等于序列的长度。

现在,小基有一个长度为 的整数序列 ,他想知道这个序列中所有可能的"排列子序列"(即满足上述条件的子序列)的价值总和是多少。请你帮他计算这个总和。

输入描述

每个测试文件包含多组测试数据。

第一行一个正整数 ,表示测试数据的组数。

对于每组测试数据,输入包含两行。

第一行一个正整数 ,表示序列 的长度。

第二行 个整数 ,表示序列 的各个元素。

保证所有测试数据中 的总和不超过

输出描述

输出 行,每行一个整数表示当前测试用例的答案,即所有"排列子序列"的价值总和。

由于答案可能很大,因此你只需要输出答案对 取模的结果。

样例1

输入:

1
3
1 1 2

输出:

6

题解

这道题目要求计算一个数组中所有"排列子序列"的价值总和。所谓的"排列子序列",指的是严格递增且恰好包含从 1 到 k 的所有整数的子序列,其价值就是长度 k。

仔细思考这个问题,可以发现,一个子序列要想成为有效的"排列子序列",必须是形如 的形式,也就是说,它必须从 1 开始,然后依次包含 ,不能有遗漏,也不能有重复。

基于这个理解,可以通过动态规划来解决问题。定义 表示数组中能形成的子序列 的个数。那么:

  • 当遇到数组中的元素 时,可以开始一个新的子序列,所以 增加 1。
  • 当遇到 时,如果已经存在子序列 ,可以把 v 接在这些子序列后面,形成新的子序列 ,所以 增加

最终,子序列 的总价值是 乘以 ,需要计算所有 k 的总和。

这种算法的时间复杂度是 ,其中 n 是数组的长度。空间复杂度也是 ,用于存储 dp 数组。

对于较大的输入,由于结果可能很大,题目要求对 1000000007 取模,这也是常见的做法。

三语言参考代码

  • Cpp
#include <bits/stdc++.h>
using namespace std;

const int MAD = 1000000007;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int test;
    cin >> test;  // 测试用例数量
    
    while (test--) {
        int size;
        cin >> size;  // 序列长度
        
        vector<int> dat(size);
        for (int i = 0; i < size; ++i) {
            cin >> dat[i];  // 输入序列
        }
        
        // dp[i]表示子序列[1,2,...,i]的数量
        vector<long long> memo(size + 1, 0);
        
        for (int i = 0; i < size; ++i) {
            int val = dat[i];
            if (val == 1) {
                // 可以新建一个只含1的子序列
                memo[1] = (memo[1] + 1) % MAD;
            } else {
                // 可以将val接到现有的子序列[1,2,...,val-1]后面
                memo[val] = (memo[val] + memo[val - 1]) % MAD;
            }
        }
        
        // 计算所有子序列的价值和
        long long ans = 0;
        for (int i = 1; i <= size; ++i) {
            ans = (ans + i * memo[i]) % MAD;
        }
        
        cout << ans << endl;
    }
    
    return 0;
}
  • Python
import sys
input = lambda:sys.stdin.readline().strip()

MAD = 10**9 + 7

def calc_value():
    T = int(input())  # 测试用例数量
    
    for _ in range(T):
        size = int(input())  # 序列长度
        seq = list(map(int, input().split()))  # 输入序列
        
        # memo[i] 表示子序列 [1,2,...,i] 的数量
        memo = [0] * (size + 1)
        
        for val in seq:
            if val == 1:
                # 可以新建一个只含1的子序列
                memo[1] = (memo[1] + 1) % MAD
            else:
                # 可以将val接到现有的子序列[1,2,...,val-1]后面
                memo[val] = (memo[val] + memo[val-1]) % MAD
        
        # 计算所有子序列的价值和
        total = 0
        for i in range(1, size + 1):
            total = (total + i * memo[i]) % MAD
            
        print(total)

if __name__ == "__main__":
    calc_value()
  • Java
import java.util.Scanner;

public class Main {
    static final int MAD = 1000000007;
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int test = scan.nextInt();  // 测试用例数量
        
        while (test-- > 0) {
            int size = scan.nextInt();  // 序列长度
            int[] seq = new int[size];
            
            for (int i = 0; i < size; i++) {
                seq[i] = scan.nextInt();  // 输入序列
            }
            
            // memo[i]表示子序列[1,2,...,i]的数量
            long[] memo = new long[size + 1];
            
            for (int i = 0; i < size; i++) {
                int val = seq[i];
                if (val == 1) {
                    // 可以新建一个只含1的子序列
                    memo[1] = (memo[1] + 1) % MAD;
                } else if (val <= size) {
                    // 可以将val接到现有的子序列[1,2,...,val-1]后面
                    memo[val] = (memo[val] + memo[val - 1]) % MAD;
                }
            }
            
            // 计算所有子序列的价值和
            long res = 0;
            for (int i = 1; i <= size; i++) {
                res = (res + i * memo[i]) % MAD;
            }
            
            System.out.println(res);
        }
    }
}

02. 优雅字符串的探索

题目内容

小柯是一位语言学家,她对字符串有着独特的研究。她定义了一种"优雅字符串"的概念:当一个字符串去重后(保留每个字符首次出现的位置,删除后续重复出现),按照原始相对顺序排列的字符是字典序单调递增的,则称这个字符串为"优雅字符串"。

例如,字符串 aabca 去重后为 abc,而 abc 在字典序上是单调递增的,因此 aabca 是一个"优雅字符串"。

现在,小柯有一个长度为 的字符串 ,她想知道这个字符串中有多少个非空子串是"优雅字符串"。请你帮她计算这个数量。

输入描述

第一行一个整数 ,表示字符串长度。

第二行一个长度为 的字符串 ,保证输入只含小写字母。

输出描述

一个整数,表示 中有多少个子串是"优雅字符串"。

样例1

输入:

5
aabca

输出:

13

题解

这道题目要求统计一个字符串中所有"优雅字符串"的数量。什么是"优雅字符串"呢?就是当一个字符串去重后(只保留每个字符首次出现的位置),剩下的字符按原来的相对顺序排列,在字典序上是单调递增的。

首先,需要明确一点:如果一个字符串的去重后结果是字典序递增的,那么它所有的子串也肯定是"优雅字符串"吗?不一定。因为当取子串时,某些字符的首次出现位置可能会改变,从而影响去重后的结果。

因此,需要对每一个可能的子串都进行检查。一种直观的思路是,对于每个可能的左端点 left,找出它右边能扩展到的最远位置 right,使得从 left 到 right 的子串都是"优雅字符串"。

可以通过逆序遍历字符串来实现这个思路:

  1. 从右向左遍历字符串,对于每个位置 left,计算以它为左端点的"优雅子串"的数量。
  2. 维护一个数组 mark,记录每个字符最后一次出现的位置。
  3. 对于当前位置 left,设定一个初始的右边界 right 和一个限制值 limit(初始都设为 n,即字符串长度)。
  4. 然后从字典序最大的字符'z'开始向前遍历到'a',更新右边界和限制值。
  5. 如果某个字符 c 出现过,且右边界 right 小于 c 最后出现的位置,那么需要更新 limit 为 min(limit, mark[c])。这是为了确保去重后的结果保持字典序递增。
  6. 计算以 left 为左端点的"优雅子串"数量,即(limit - left),累加到结果中。

这个算法的时间复杂度是 ,其中 n 是字符串长度,因为对于每个位置,都需要遍历 26 个小写字母。空间复杂度是 ,因为只需要一个固定大小的数组来记录每个字符最后出现的位置。

三语言参考代码

  • Cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int len;
    string data;
    cin >> len >> data;
    
    long long cnt = 0;  // 记录优雅子串的总数
    vector<int> mark(26, -1)

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

月入泉:假的,要你简历,然后说你简历的不足,让你报班的
点赞 评论 收藏
分享
03-24 13:24
已编辑
江西农业大学 后端工程师
最近或许大家都听说xxxx厂裁员,无论前端,后端,大数据,测试,运维,人人可危,&nbsp;“前端死了,后端也死了,JAVA崩盘了,你们这群搞大模型的真是码奸”这次AI真的会让我们无路可走吗????????太阳底下已经没有新鲜事了旧的生产力的消失,必然有新的生产力诞生马车夫消失&nbsp;→&nbsp;汽车司机、修车工、石油工业诞生,从业人数是马车夫的百倍手工纺织女工消失&nbsp;→&nbsp;纺织机械工程师、面料设计师诞生,纺织品产量提升百倍2007年苹果开放&nbsp;App&nbsp;Store,&quot;移动端开发者&quot;这个职业压根不存在。八年后,全球应用经济规模突破&nbsp;1000亿美元,凭空诞生了数百万开发者岗位。每一次&quot;这次真的完了...
二十岁的编程男神王大...:那这个时代是什么时代呢? 是全员agent的时代,是前端+AI,后端+AI的时代,AI已经融入了项目生命周期的的每一个角落,那我最近在做的东西举例,检查BUG时,我们会用codex,CC等工具的skill去check,效果好还能直接fix,测试的时候,apifox等工具已经有了AI落地的改造,CI/CD阶段,我们会根据hook去跑AI check脚本,就连不少中间件,也迎来了AI落地的改造,(AI网关,AI在MQ中的运用),都可以去了解下 另外记着,这些东西不是意义,工作只是谋生的一个手段,ai是让开发提效了,但是呢,原先一周的工作流程压缩到了两天内,同时低级的都裁员了,只有高级的去维护,你看似写的大义凛然,或许那天你也会成为你文章里面拒绝往前走的人,你才大二,面对技术有热情是对的
AI求职实录
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
正在热议
更多
# 一张图晒出你司的标语 #
4278次浏览 75人参与
# AI面会问哪些问题? #
27722次浏览 552人参与
# 开放七大实习专项,百度暑期实习值得冲吗 #
15190次浏览 221人参与
# 你的实习产出是真实的还是包装的? #
20121次浏览 342人参与
# 找AI工作可以去哪些公司? #
9058次浏览 233人参与
# 春招至今,你的战绩如何? #
64941次浏览 580人参与
# 厦门银行科技岗值不值得投 #
8003次浏览 188人参与
# 从事AI岗需要掌握哪些技术栈? #
8891次浏览 304人参与
# 中国电信笔试 #
31989次浏览 292人参与
# 你做过最难的笔试是哪家公司 #
33385次浏览 231人参与
# 投递几十家公司,到现在0offer,大家都一样吗 #
340781次浏览 2174人参与
# 阿里笔试 #
178513次浏览 1315人参与
# 哪些公司真双非友好? #
69573次浏览 289人参与
# 机械人避雷的岗位/公司 #
62703次浏览 393人参与
# 第一份工作一定要去大厂吗 #
14543次浏览 122人参与
# 金三银四,你的春招进行到哪个阶段了? #
22072次浏览 280人参与
# 为了减少AI幻觉,你注入过哪些设定? #
26246次浏览 310人参与
# 沪漂/北漂你觉得哪个更苦? #
9812次浏览 193人参与
# 应届生第一份工资要多少合适 #
20680次浏览 86人参与
# HR最不可信的一句话是__ #
6208次浏览 114人参与
# AI时代,哪个岗位还有“活路” #
11489次浏览 341人参与
# 春招你拿到offer了吗 #
831177次浏览 9987人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务