【阿里云笔试题汇总】2024-04-20-阿里云春招笔试题-三语言题解(CPP/Python/Java)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员

✨ 本系列打算持续跟新阿里云近期的春秋招笔试题汇总~

💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导

👏 感谢大家的订阅➕ 和 喜欢💗

📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。

🎊 01.最短等价子串

问题描述

K 小姐接到了一个字符串处理的任务。给定一个长度为 n n n 的小写字母字符串 S S S,定义字符串的权值为相邻两个字符相同的对数。例如,字符串 aaabbc 的权值为 3。

现在,K 小姐需要在字符串 S S S 中找到一个长度最短的子串,使得该子串的权值恰好等于 k k k。你能帮助她完成这个任务吗?

输入格式

第一行包含两个正整数 n n n k k k,分别表示字符串 S S S 的长度和目标权值。

第二行包含一个长度为 n n n 的字符串 S S S,仅由小写字母组成。

输出格式

输出一个整数,表示满足条件的最短子串的长度。如果不存在这样的子串,则输出 -1。

样例输入

6 2
aaabbc

样例输出

3

数据范围

1 ≤ k < n ≤ 3 × 1 0 5 1 \leq k < n \leq 3 \times 10^5 1k<n3×105

题解

本题可以使用双指针算法求解。我们用两个指针 i i i j j j 分别表示当前子串的起点和终点。初始时,两个指针都指向字符串的起点。

我们不断地向右移动指针 j j j,直到当前子串的权值大于等于 k k k。此时,我们尝试向右移动指针 i i i,缩小当前子串的长度,直到子串的权值恰好等于 k k k。在这个过程中,我们记录下满足条件的最短子串长度。

重复这个过程,直到指针 j j j 到达字符串的末尾。最终得到的最短子串长度即为答案。

参考代码

  • Python
n, k = map(int, input().split())
s = input()

res = 0
ans = n + 1
i = 0
j = 1

while i < n:
    if i > 0 and s[i] == s[i - 1]:
        res -= 1
    
    while j < n and res < k:
        if s[j] == s[j - 1]:
            res += 1
        j += 1
    
    if res == k:
        ans = min(ans, j - i)
    
    i += 1

print(ans if ans != n + 1 else -1)
  • Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        String s = sc.next();
        
        int res = 0;
        int ans = n + 1;
        int i = 0;
        int j = 1;
        
        while (i < n) {
            if (i > 0 && s.charAt(i) == s.charAt(i - 1)) {
                res--;
            }
            
            while (j < n && res < k) {
                if (s.charAt(j) == s.charAt(j - 1)) {
                    res++;
                }
                j++;
            }
            
            if (res == k) {
                ans = Math.min(ans, j - i);
            }
            
            i++;
        }
        
        System.out.println(ans == n + 1 ? -1 : ans);
    }
}
  • Cpp
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    
    int res = 0;
    int ans = n + 1;
    int i = 0;
    int j = 1;
    
    while (i < n) {
        if (i > 0 && s[i] == s[i - 1]) {
            res--;
        }
        
        while (j < n && res < k) {
            if (s[j] == s[j - 1]) {
                res++;
            }
            j++;
        }
        
        if (res == k) {
            ans = min(ans, j - i);
        }
        
        i++;
    }
    
    cout << (ans == n + 1 ? -1 : ans) << endl;
    
    return 0;
}

🎀 02.完美排列对

问题描述

K 小姐是一位数学爱好者,她正在研究排列的性质。给定一个长度为 n n n 的整数数组 a a a,K 小姐希望构造两个长度为 n n n 的排列 p p p q q q,满足以下条件:

  1. 对于任意 1 ≤ i ≤ n 1 \leq i \leq n 1in,都有 p i + q i = n + 1 p_i + q_i = n + 1 pi+qi=n+1
  2. 对于任意 1 ≤ i ≤ n 1 \leq i \leq n 1in,都有 ∣ p i − q i ∣ = a i |p_i - q_i| = a_i piqi=ai

你能帮助 K 小姐找到这样的排列对吗?

注:排列是指一个长度为 n n n 的数组,其中 1 1 1 n n n 每个元素恰好出现一次。

输入格式

第一行包含一个正整数 n n n,表示数组 a a a 的长度。

第二行包含 n n n 个整数,表示数组 a a a 的元素,相邻两个整数之间用单个空格隔开。

输出格式

如果不存在满足条件的排列对,则输出一行,包含一个整数 − 1 -1 1

否则输出两行,第一行包含 n n n 个整数,表示排列 p p p;第二行包含 n n n 个整数,表示排列 q q q。相邻两个整数之间用单个空格隔开。如果存在多组解,输出任意一组即可。

样例输入

3
2 2 0

样例输出

1 3 2
3 1 2

数据范围

1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105
0 ≤ a i ≤ n − 1 0 \leq a_i \leq n-1 0ain1

题解

根据题目条件,可以得到 p i p_i pi q i q_i qi 的关系:

  1. p i + q i = n + 1 p_i + q_i = n + 1 pi+qi=n+1
  2. ∣ p i − q i ∣ = a i |p_i - q_i| = a_i piqi=ai

由条件 1 可知, p i p_i pi q i q_i qi 的和是固定的,都等于 n + 1 n+1 n+1。因此,可以预处理出所有可能的 ( p i , q i ) (p_i, q_i) (pi,qi) 对,并按照 ∣ p i − q i ∣ |p_i - q_i| piqi 的值进行分组。

接下来,将输入的数组 a a a 排序,然后与预处理得到的 ∣ p i − q i ∣ |p_i - q_i| piqi 的值进行比较。如果两个数组排序后不相同,说明无解,直接输出 − 1 -1 1

如果两个数组排序后相同,则按照 a a a 数组的顺序,依次从对应的 ( p i , q i ) (p_i, q_i) (pi,qi) 分组中取出一对值,即可得到满足条件的排列 p p p q q q

时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),空间复杂度为 O ( n ) O(n) O(n)

参考代码

  • Python
from collections import defaultdict

n = int(input())
a = list(map(int, input().split()))
b = a[:]
c = [abs(i - (n - 1 - i)) for i in range(n)]
mp = defaultdict(list)
for i in range(n):
    mp[c[i]].append((i, n - 1 - i))

b.sort()
c.sort()

if b != c:
    print(-1)
else:
    p = [0] * n
    q = [0] * n
    for i in range(n):
        x, y = mp[a[i]].pop()
        p[i] = x + 1
        q[i] = y + 1
    print(*p)
    print(*q)
  • Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        int[] b = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
            b[i] = a[i];
        }
        
        int[] c = new int[n];
        Map<Integer, List<int[]>> mp = new HashMap<>();
        for (int i = 0; i < n; i++) {
            c[i] = Math.abs(i - (n - 1 - i));
            mp.computeIfAbsent(c[i], k -> new ArrayList<>()).add(new int[]{i, n - 1 - i});
        }
        
        Arrays.sort(b);
        Arrays.sort(c);
        
        if (!Arrays.equals(b, c)) {
            System.out.println(-1);
        } else {
            int[] p = new int[n];
            int[] q = new int[n];
            for (int i = 0; i < n; i++) {
                int[] pair = mp.get(a[i]).remove(mp.get(a[i]).size() - 1);
                p[i] = pair[0] + 1;
                q[i] = pair[1] + 1;
            }
            for (int i = 0; i < n; i++) {
                System.out.print(p[i] + " ");
            }
            System.out.println();
            for (int i = 0; i < n; i++) {
                System.out.print(q[i] + " ");
            }
        }
    }
}
  • Cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    auto b = a;
    vector<int> c(n);
    unordered_map<int, vector<pair<int, int>>> mp;
    for (int i = 0; i < n; i++) {
        c[i] = abs(i - (n - 1 - i));
        mp[c[i]].push_back({i, n - 1 - i});
    }
    
    sort(b.begin(), b.end());
    sort(c.begin(), c.end());
    
    if (b != c) {
        cout << -1 << endl;
    } else {
        vector<int> p(n), q(n);
        for (int i = 0; i < n; i++) {
            auto pair = mp[a[i]].back();
            mp[a[i]].pop_back();
            p[i] = pair.first + 1;
            q[i] = pair.second + 1;
        }
        for (int i = 0; i < n; i++) {
            cout << p[i] << ' ';
        }
        cout << endl;
        for (int i = 0; i < n; i++) {
            cout << q[i] << ' ';
        }
    }
    
    return 0;
}

💝 03.K小姐的森林管理

题目描述

K小姐是一位热爱大自然的女孩,她拥有一片由 n n n 棵树组成的森林。这片森林可以看作一个无向图,树木之间通过 m m m 条小路相连。

为了更好地管理这片森林,K小姐可以进行以下两种操作:

  1. 在任意一棵现有的树上种植一棵新的小树苗,并用一条小路将其与原来的树相连。
  2. 移除一棵叶子节点(即度数为1的节点)以及与之相连的小路。

K小姐的目标是使森林中的每个连通块(即互相连通的树木集合)包含相同数量的树木。你的任务是帮助K小姐计算出达成目标状态所需的最少操作次数。

输入格式

第一行包含两个正整数 n n n m m m,分别表示森林中树木的数量和小路的数量。

接下来 m m m 行,每行包含两个正整数 u u u v v v,表示编号为 u u u v v v 的两棵树之间有一条小路相连。

输出格式

输出一个整数,表示K小姐达成目标状态所需的最少操作次数。

样例输入

4 2
1 2
1 3

样例输出

2

数据范围

1 ≤ m < n ≤ 1 0 5 1 \le m < n \le 10^5 1m<n105

保证给定的无向图是一个森林。

题解

本题可以通过并查集和前缀和的思想来解决。

首先,我们可以使用并查集来处理出森林中每个连通块的大小。这样就将问题转化为了使数组中所有元素相等的最小代价问题。

接下来,我们可以对表示连通块大小的数组进行排序,然后用前缀和计算出前 i i i 个元素的和。这样就可以在 O ( c n t ) O(cnt) O(cnt) 的时间复杂度内(其中 c n t cnt cnt 为连通块的个数)计算出将所有连通块的大小变为 x x x 的代价。

最后,我们遍历所有可能的连通块大小 x x x,就能得到最小的操作次数。

总时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

参考代码

  • Python
def find(x):
    if p[x] != x:
        p[x] = find(p[x])
    return p[x]

n, m = map(int, input().split())
p = list(range(n + 1))
num = [1] * (n + 1)

for _ in range(m):
    a, b = map(int, input().split())
    px, py = find(a), find(b)
    if px != py:
        p[px] = py
        num[py] += num[px]

c = [num[i] for i in range(1, n + 1) if p[i] == i]
cnt = len(c)
c.sort()

sum = [0] * cnt
for i in range(cnt):
    sum[i] = c[i]
    if i > 0:
        sum[i] += sum[i - 1]

res = n
for i in range(cnt):
    res = min(res, (i + 1) * c[i] - sum[i] + sum[cnt - 1] - sum[i] - (cnt - i - 1) * c[i])

print(res)
  • Java
import java.util.*;

public class Main {
    static int[] p, num, c, sum;
    
    static int find(int x) {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        p = new int[n + 1];
        num = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            p[i] = i;
            num[i] = 1;
        }
        
        for (int i = 0; i < m; i++) {
            int a = sc.nextInt(), b = sc.nextInt();
            int px = find(a), py = find(b);
            if (px != py) {
                p[px] = py;
                num[py] += num[px];
            }
        }
        
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (p[i] == i) cnt++;
        }
        c = new int[cnt];
        sum = new int[cnt];
        cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (p[i] == i) c[cnt++] = num[i];
        }
        Arrays.sort(c);
        
        for (int i = 0; i < cnt; i++) {
            sum[i] = c[i];
            if (i > 0) sum[i] += sum[i - 1];
        }
        
        int res = n;
        for (int i = 0; i < cnt; i++) {
            res = Math.min(res, (i + 1) * c[i] - sum[i] + sum[cnt - 1] - sum[i] - (cnt - i - 1) * c[i]);
        }
        System.out.println(res);
    }
}
  • Cpp
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1e5 + 7;

int n, m, p[N], num[N], c[N], sum[N];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        p[i] = i;
        num[i] = 1;
    }
    
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        int px = find(a), py = find(b);
        if (px != py) {
            p[px] = py;
            num[py] += num[px];
        }
    }
    
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (p[i] == i) c[cnt++] = num[i];
    }
    sort(c, c + cnt);
    
    for (int i = 0; i < cnt; i++) {
        sum[i] = c[i];
        if (i > 0) sum[i] += sum[i - 1];
    }
    
    int res = n;
    for (int i = 0; i < cnt; i++) {
        res = min(res, (i + 1) * c[i] - sum[i] + sum[cnt - 1] - sum[i] - (cnt - i - 1) * c[i]);
    }
    cout << res << endl;
    
    return 0;
}

写在最后

📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。

在这里插入图片描述
在这里插入图片描述

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值