【蚂蚁笔试题汇总】[全网首发] 2024-04-06-蚂蚁春招笔试题-三语言题解(CPP/Python/Java)

🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员

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

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

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

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

01.卢小姐的字母重排

问题描述

卢小姐在研究一种古老的密码学方法,这种方法通过分析字符串的循环节来解密信息。例如,字符串 “abcabc” 的循环节是 “abc”,而 “aba” 的循环节是 “aba” 本身。卢小姐现在手头有一个字符串 s s s,她想通过重新排列这个字符串,使得新字符串的循环节长度最短。她需要你的帮助来找出这个最短循环节,并输出重排后的字符串。如果有多种重排方式,她希望得到字典序最小的一种。

输入格式

第一行输入一个长度不超过 1 0 5 10^5 105 的字符串 s s s,该字符串只包含小写字母。

输出格式

输出一行,表示重排后的字符串。如果存在多种重排方案,输出字典序最小的方案。

样例输入

cabbac

样例输出

abcabc

解释

在这个例子中,“abc” 是长度最短的循环节之一,且可以证明 “abcabc” 是字典序最小的重排方案。

数据范围

  • 字符串长度不超过 1 0 5 10^5 105
  • 字符串只包含小写字母。

题解

要解决这个问题,我们首先需要计算字符串中每个字符出现的次数,并找到这些次数的最大公约数(GCD)。这个 GCD 将决定循环节的长度。接着,我们构造一个循环节,使得每个字符在循环节中出现的次数等于它的出现次数除以 GCD。最后,我们重复这个循环节 GCD 次,以构造出最终的字符串。如果有多种构造方式,我们选择字典序最小的方式,即按照字母顺序添加字符。

参考代码

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

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    string s;
    cin >> s;
    map<char, int> freq;
    for (char ch : s) freq[ch]++;
    int g = 0;
    for (auto it : freq) g = gcd(g, it.second);
    string res = "";
    for (int i = 0; i < 26; i++) {
        char ch = 'a' + i;
        if (freq.count(ch)) res += string(freq[ch] / g, ch);
    }
    string final_res = res;
    for (int i = 1; i < g; i++) final_res += res;
    cout << final_res << "\n";
    return 0;
}
  • Python
from collections import Counter
from math import gcd
from functools import reduce

def find_gcd(list):
    x = reduce(gcd, list)
    return x

s = input()
freq = Counter(s)
g = find_gcd(freq.values())
res = ''.join([ch * (freq[ch] // g) for ch in sorted(freq)])
final_res = res * g
print(final_res)
  • Java
import java.util.*;

public class Main {
    public static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.next();
        Map<Character, Integer> freq = new HashMap<>();
        for (char ch : s.toCharArray()) freq.put(ch, freq.getOrDefault(ch, 0) + 1);
        int g = freq.values().stream().reduce(0, Main::gcd);
        StringBuilder res = new StringBuilder();
        freq.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(entry -> {
                for (int i = 0; i < entry.getValue() / g; i++) res.append(entry.getKey());
            });
        String finalRes = res.toString().repeat(g);
        System.out.println(finalRes);
    }
}

02.LYA的宠物摆放问题

问题描述

LYA有一个 3 × 3 3 \times 3 3×3 的矩阵花园,她准备在花园里放置 x x x 只小狗和 y y y 只小猫。为了避免小动物们打架,要求小猫之间、小狗之间不能放在相邻的位置(上下左右都算相邻)。LYA想知道,一共有多少种不同的放置方案呢?

输入格式

输入包含两个整数 x x x y y y,用空格隔开,分别表示小狗和小猫的数量。
0 ≤ x + y ≤ 9 0 \leq x+y \leq 9 0x+y9

输出格式

输出一个整数,表示放置方案的总数。

样例输入

1 0

样例输出

9

数据范围

0 ≤ x + y ≤ 9 0 \leq x+y \leq 9 0x+y9

题解

这是一道典型的搜索题,我们可以用DFS来解决。用一个一维数组 g r i d grid grid 来表示花园的状态,0表示空位,1表示放小狗,2表示放小猫。

在DFS函数中,我们用变量 p o s pos pos 表示当前搜索到的位置, d o g dog dog c a t cat cat 分别表示已经放置的小狗和小猫的数量。当 p o s pos pos 等于9时,说明已经搜索完整个花园,此时判断放置的小狗和小猫数量是否符合要求,并且当前放置方案是否合法,如果合法则将答案加1。

在搜索过程中,对于每个位置,我们有三种选择:放小狗、放小猫或者不放。如果选择放小狗,则 d o g dog dog 加1;如果选择放小猫,则 c a t cat cat 加1。然后递归搜索下一个位置。

为了判断当前放置方案是否合法,我们可以写一个 i s _ v a l i d is\_valid is_valid 函数,枚举每个位置,如果该位置放了小动物,则判断它的上下左右四个相邻位置是否放了相同的小动物,如果是则说明不合法。

时间复杂度 O ( 3 9 ) O(3^9) O(39),空间复杂度 O ( 9 ) O(9) O(9)

参考代码

  • Python
def dfs(pos, dog, cat, grid):
    if pos == 9:
        return int(dog == n1 and cat == n2 and is_valid(grid))
    
    res = 0
    for i in range(3):
        grid[pos] = i
        if i == 0:
            res += dfs(pos + 1, dog, cat, grid)
        elif i == 1:
            res += dfs(pos + 1, dog + 1, cat, grid)
        else:
            res += dfs(pos + 1, dog, cat + 1, grid)
    
    return res

def is_valid(grid):
    for i in range(9):
        if grid[i] == 0:
            continue
        r, c = i // 3, i % 3
        for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            nr, nc = r + dr, c + dc
            if 0 <= nr < 3 and 0 <= nc < 3 and grid[nr * 3 + nc] == grid[i]:
                return False
    return True

n1, n2 = map(int, input().split())
grid = [0] * 9
print(dfs(0, 0, 0, grid))
  • Java
import java.util.Scanner;

public class Main {
    static int n1, n2;
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n1 = sc.nextInt();
        n2 = sc.nextInt();
        int[] grid = new int[9];
        System.out.println(dfs(0, 0, 0, grid));
    }
    
    static int dfs(int pos, int dog, int cat, int[] grid) {
        if (pos == 9) {
            return (dog == n1 && cat == n2 && isValid(grid)) ? 1 : 0;
        }
        
        int res = 0;
        for (int i = 0; i < 3; i++) {
            grid[pos] = i;
            if (i == 0) {
                res += dfs(pos + 1, dog, cat, grid);
            } else if (i == 1) {
                res += dfs(pos + 1, dog + 1, cat, grid);
            } else {
                res += dfs(pos + 1, dog, cat + 1, grid);
            }
        }
        
        return res;
    }
    
    static boolean isValid(int[] grid) {
        for (int i = 0; i < 9; i++) {
            if (grid[i] == 0) {
                continue;
            }
            int r = i / 3, c = i % 3;
            int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
            for (int[] dir : dirs) {
                int nr = r + dir[0], nc = c + dir[1];
                if (nr >= 0 && nr < 3 && nc >= 0 && nc < 3 && grid[nr * 3 + nc] == grid[i]) {
                    return false;
                }
            }
        }
        return true;
    }
}
  • Cpp
#include <iostream>
using namespace std;

int n1, n2;

int dfs(int pos, int dog, int cat, int grid[]) {
    if (pos == 9) {
        return (dog == n1 && cat == n2 && isValid(grid)) ? 1 : 0;
    }
    
    int res = 0;
    for (int i = 0; i < 3; i++) {
        grid[pos] = i;
        if (i == 0) {
            res += dfs(pos + 1, dog, cat, grid);
        } else if (i == 1) {
            res += dfs(pos + 1, dog + 1, cat, grid);
        } else {
            res += dfs(pos + 1, dog, cat + 1, grid);
        }
    }
    
    return res;
}

bool isValid(int grid[]) {
    for (int i = 0; i < 9; i++) {
        if (grid[i] == 0) {
            continue;
        }
        int r = i / 3, c = i % 3;
        int dirs[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        for (auto& dir : dirs) {
            int nr = r + dir[0], nc = c + dir[1];
            if (nr >= 0 && nr < 3 && nc >= 0 && nc < 3 && grid[nr * 3 + nc] == grid[i]) {
                return false;
            }
        }
    }
    return true;
}

int main() {
    cin >> n1 >> n2;
    int grid[9];
    cout << dfs(0, 0, 0, grid) << endl;
    return 0;
}

03.LYA的森林探险

问题描述

LYA在一个由 n n n 个点 m m m 条边组成的森林中探险。这个森林中任意两个点要么不连通,要么有且仅有一条路径。LYA有 q q q 次询问,每次询问分为两类:

  1. 输入一个点 p p p,请输出 p p p 所在的连通块中任意两点的最大距离。

  2. 输入两个点 x x x y y y,如果 x x x y y y 不连通,将 x x x y y y 所在的连通块连通,需要保证连通后这个连通块的最长路径最小。如果 x x x y y y 已经连通,则不做任何操作。

请你帮助LYA解决这些问题。

输入格式

第一行三个整数 n , m , q n,m,q n,m,q,表示点数、边数和询问数。

接下来 m m m 行,每行两个整数 u , v u,v u,v,表示 u u u v v v 之间有一条边,保证输入是一个森林。

接下来 q q q 行,每行表示一个询问:

  • 如果第一个数是 1 1 1,则后面一个数是 p p p,表示询问 p p p 所在的连通块中任意两点的最大距离。
  • 如果第一个数是 2 2 2,则后面两个数是 x , y x,y x,y,表示将 x x x y y y 所在的连通块连通,需要保证连通后这个连通块的最长路径最小。如果 x x x y y y 已经连通,则不做任何操作。

输出格式

对于每个第一类询问,输出一行一个整数,表示 p p p 所在的连通块中任意两点的最大距离。

样例输入

5 2 5
1 2
3 4
1 1
1 3
1 5
2 1 3
1 1

样例输出

1
1
0
3

数据范围

1 ≤ m < n ≤ 1 0 5 1 \leq m < n \leq 10^5 1m<n105
1 ≤ q ≤ 1 0 5 1 \leq q \leq 10^5 1q105
1 ≤ u , v , p , x , y ≤ n 1 \leq u,v,p,x,y \leq n 1u,v,p,x,yn

题解

这道题可以用并查集和树形DP来解决。

首先,我们用并查集来维护连通块。对于每个连通块,我们用一个变量 m a x D i a maxDia maxDia 来记录该连通块中任意两点的最大距离。

然后,我们遍历每个连通块,对其进行树形DP,求出该连通块中任意两点的最大距离,更新 m a x D i a maxDia maxDia

树形DP的过程如下:对于每个点 u u u,我们找到它的两个最深子树的深度 d 1 d_1 d1 d 2 d_2 d2,则经过 u u u 的最长路径为 d 1 + d 2 d_1+d_2 d1+d2,我们用它来更新答案。

对于第一类询问,我们直接输出该点所在连通块的 m a x D i a maxDia maxDia 即可。

对于第二类询问,我们先判断两个点是否已经连通,如果不连通,我们就将它们所在的连通块合并。合并时,我们需要更新新的连通块的 m a x D i a maxDia maxDia,新的 m a x D i a maxDia maxDia 为两个连通块的 m a x D i a maxDia maxDia 的较大值,或者是它们的 m a x D i a maxDia maxDia 的一半上取整后加上 1 1 1

时间复杂度 O ( n + m + q log ⁡ n ) O(n+m+q\log n) O(n+m+qlogn),空间复杂度 O ( n + m ) O(n+m) O(n+m)

参考代码

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

const int MAXN = 100010;
vector<int> maxDia(MAXN, 0);
vector<int> par(MAXN);
vector<int> sz(MAXN, 1);

void initDSU(int n) {
    iota(par.begin(), par.end(), 0);
}

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

bool join(int a, int b) {
    if (maxDia[a] < maxDia[b]) swap(a, b);
    a = find(a);
    b = find(b);
    if (a == b) return false;
    par[b] = a;
    sz[a] += sz[b];
    maxDia[a] = max(maxDia[a], (maxDia[a] + 1) / 2 + (maxDia[b] + 1) / 2 + 1);
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, m, q;
    cin >> n >> m >> q;
    initDSU(n);
    vector<vector<int>> gr(n + 1);

    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        gr[u].push_back(v);
        gr[v].push_back(u);
        join(find(u), find(v));
    }

    set<int> uniq;
    for (int i = 1; i <= n; ++i) {
        uniq.insert(find(i));
    }

    function<int(int, int)> dfs = [&](int u, int p) -> int {
        int d1 = 0, d2 = 0;
        for (int v : gr[u]) {
            if (v != p) {
                int d = dfs(v, u) + 1;
                if (d >= d1) {
                    d2 = d1;
                    d1 = d;
                } else if (d > d2) {
                    d2 = d;
                }
            }
        }
        maxDia[find(u)] = max(maxDia[find(u)], d1 + d2);
        return d1;
    };

    for (int rt : uniq) {
        dfs(rt, -1);
    }

    for (int i = 0; i < q; ++i) {
        int op;
        cin >> op;
        if (op == 1) {
            int x;
            cin >> x;
            cout << maxDia[find(x)] << "\n";
        } else {
            int a, b;
            cin >> a >> b;
            join(find(a), find(b));
        }
    }

    return 0;
}
  • Java
import java.util.*;
import java.io.*;

public class GraphDiam {
    static final int MAXN = 100010;
    static int[] maxDia = new int[MAXN];
    static int[] par = new int[MAXN];
    static int[] sz = new int[MAXN];
    static List<List<Integer>> gr = new ArrayList<>();

    static {
        for (int i = 0; i < MAXN; i++) {
            par[i] = i;
            sz[i] = 1;
            gr.add(new ArrayList<>());
        }
    }

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

    static boolean join(int a, int b) {
        if (maxDia[a] < maxDia[b]) {
            int tmp = a;
            a = b;
            b = tmp;
        }
        a = find(a);
        b = find(b);
        if (a == b) return false;
        par[b] = a;
        sz[a] += sz[b];
        maxDia[a] = Math.max(maxDia[a], (maxDia[a] + 1) / 2 + (maxDia[b] + 1) / 2 + 1);
        return true;
    }

    static int dfs(int u, int p) {
        int d1 = 0, d2 = 0;
        for (int v : gr.get(u)) {
            if (v != p) {
                int d = dfs(v, u) + 1;
                if (d >= d1) {
                    d2 = d1;
                    d1 = d;
                } else if (d > d2) {
                    d2 = d;
                }
            }
        }
        maxDia[find(u)] = Math.max(maxDia[find(u)], d1 + d2);
        return d1;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int q = Integer.parseInt(st.nextToken());

        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            gr.get(u).add(v);
            gr.get(v).add(u);
            join(find(u), find(v));
        }

        Set<Integer> uniq = new HashSet<>();
        for (int i = 1; i <= n; i++) {
            uniq.add(find(i));
        }

        for (int rt : uniq) {
            dfs(rt, -1);
        }

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        for (int i = 0; i < q; i++) {
            st = new StringTokenizer(br.readLine());
            int op = Integer.parseInt(st.nextToken());
            if (op == 1) {
                int x = Integer.parseInt(st.nextToken());
                bw.write(maxDia[find(x)] + "\n");
            } else {
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
                join(find(a), find(b));
            }
        }
        bw.flush();
    }
}
  • Python
from collections import defaultdict

MAXN = 100010
maxDia = [0] * MAXN
par = list(range(MAXN))
sz = [1] * MAXN
gr = defaultdict(list)

def find(x):
    if par[x] != x:
        par[x] = find(par[x])
    return par[x]

def join(a, b):
    if maxDia[a] < maxDia[b]:
        a, b = b, a
    a = find(a)
    b = find(b)
    if a == b:
        return False
    par[b] = a
    sz[a] += sz[b]
    maxDia[a] = max(maxDia[a], (maxDia[a] + 1) // 2 + (maxDia[b] + 1) // 2 + 1)
    return True

def dfs(u, p):
    d1, d2 = 0, 0
    for v in gr[u]:
        if v != p:
            d = dfs(v, u) + 1
            if d >= d1:
                d2, d1 = d1, d
            elif d > d2:
                d2 = d
    maxDia[find(u)] = max(maxDia[find(u)], d1 + d2)
    return d1

n, m, q = map(int, input().split())

for _ in range(m):
    u, v = map(int, input().split())
    gr[u].append(v)
    gr[v].append(u)
    join(find(u), find(v))

uniq = set(find(i) for i in range(1, n + 1))

for rt in uniq:
    dfs(rt, -1)

for _ in range(q):
    qry = list(map(int, input().split()))
    op = qry[0]
    if op == 1:
        x = qry[1]
        print(maxDia[find(x)])
    else:
        a, b = qry[1], qry[2]
        join(find(a), find(b))

写在最后

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

在这里插入图片描述

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
2023年3月11日,美团春季招聘笔试中共包含五道编程题目。以下是对每道题目的简要说明: 1. 题目一:这道题目要求解决一个数字统计的问题。可能涉及到的知识点包括数据结构、循环和条件判断等。解决问题的思路可能是使用字典等数据结构来保存统计结果,并使用循环逐个读取输入数据并进行统计。 2. 题目二:这道题目可能是一个字符串处理的问题。需要使用字符串的方法进行操作,如提取、拼接、查找和替换等。可能的解决思路包括使用正则表达式、切片和遍历等。 3. 题目:这道题目可能涉及到算法和数据结构的知识。可能是一道涉及到数组、链表、树等数据结构的问题。解决思路可能包括遍历、递归、搜索和排序等。 4. 题目四:这道题目可能是一个动态规划的问题。需要根据给定的条件和规则,通过动态规划的方式求解问题。解决思路包括定义状态和转移方程,使用递推或记忆化搜索进行求解。 5. 题目五:这道题目可能是一个图论或网络问题。需要根据给定的图或网络结构,解决一个相关的问题。可能涉及到广度优先搜索、深度优先搜索、最短路径等知识。解决思路可能包括使用图或网络的相关算法进行求解。 以上只是对这五道编程题目的一些可能情况进行的简要描述,具体的题目内容可能会有所不同。希望这些信息能对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值