【阿里淘天笔试题汇总】2024-04-03-阿里淘天春招笔试题(第二套)-三语言题解(CPP/Python/Java)

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

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

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

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

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

01.卢小姐的幸运数字

问题描述

卢小姐最近在研究一种幸运数字:任何一个整数,只要它由其他整数拼接而成,并且这个拼接后的整数能被 3 整除,她就认为它是幸运的。现在,卢小姐手头有一个整数序列,她想知道序列中某些特定区间内的数字拼接起来后是否构成了她的幸运数字。

输入格式

第一行包含两个整数 n , q n, q n,q,表示整数序列的长度和卢小姐拟进行的询问次数。

第二行包含 n n n 个整数,表示整数序列 a i a_i ai

接下来的 q q q 行,每行包含两个整数,表示卢小姐询问的区间 [ l , r ] [l, r] [l,r]

输出格式

对于每个询问,如果区间内的整数拼接起来是 3 的倍数,则输出 YES,否则输出 NO

样例输入

3 2
11 45 14
1 3
2 2

样例输出

NO
YES

数据范围

对于所有测试数据,满足 1 ≤ n , q ≤ 1 0 5 1 \leq n, q \leq 10^5 1n,q105 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

题解

卢小姐的问题可以通过计算区间内所有数的和来简化。由于一个数字如果要被 3 整除,那么该数字的每一位数加起来的和也必须能被 3 整除。我们可以先计算出整数序列的前缀和,然后通过前缀和来快速得到任意区间内所有数的和,从而判断该区间内的数拼接起来是否能被 3 整除。

参考代码

  • Python
n, q = map(int, input().split())
a = list(map(int, input().split()))

prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
    prefix_sum[i] = prefix_sum[i - 1] + a[i - 1] % 3

for _ in range(q):
    l, r = map(int, input().split())
    if (prefix_sum[r] - prefix_sum[l - 1]) % 3 == 0:
        print("YES")
    else:
        print("NO")
  • Java
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int q = sc.nextInt();
        int[] a = new int[n + 1];
        int[] prefixSum = new int[n + 1];

        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
            prefixSum[i] = prefixSum[i - 1] + a[i] % 3;
        }

        for (int i = 0; i < q; i++) {
            int l = sc.nextInt();
            int r = sc.nextInt();
            if ((prefixSum[r] - prefixSum[l - 1]) % 3 == 0) {
                System.out.println("YES");
            } else {
                System.out.println("NO");
            }
        }
    }
}
  • Cpp
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 1), prefixSum(n + 1, 0);

    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        prefixSum[i] = prefixSum[i - 1] + a[i] % 3;
    }

    while (q--) {
        int l, r;
        cin >> l >> r;
        if ((prefixSum[r] - prefixSum[l - 1]) % 3 == 0) {
            cout << "YES\n";
        }
      	else
          	cout << "NO\n";
    }
}

02.灯泡大作战

问题描述

K小姐有一条由 n n n 个灯泡组成的灯带,一开始所有灯泡都是熄灭的。每个灯泡都有自己的颜色,用一个整数表示。K小姐可以进行以下两种操作之一:

  1. 选择一个熄灭的灯泡,将其点亮。
  2. 选择一个区间 [ l , r ] [l, r] [l,r],如果区间两端的灯泡颜色不同且都是熄灭的,那么可以将区间内所有灯泡点亮。

K小姐想知道,最少需要多少次操作才能将所有灯泡点亮?

输入格式

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

接下来对于每组测试数据:

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

第二行包含 n n n 个空格分开的正整数 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an,表示每个灯泡的颜色。

输出格式

对于每组测试数据,输出一行一个整数,表示将所有灯泡点亮的最少操作次数。

样例输入

2
3
1 2 1
2
1 1

样例输出

2
2

数据范围

  • 1 ≤ T ≤ 1 0 4 1 \leq T \leq 10^4 1T104
  • 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105
  • 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

题解

本题可以使用动态规划来求解。设 f [ i ] f[i] f[i] 表示将前 i i i 个灯泡点亮的最少操作次数。对于第 i i i 个灯泡,我们可以考虑两种情况:

  1. 单独将第 i i i 个灯泡点亮,此时操作次数为 f [ i − 1 ] + 1 f[i-1] + 1 f[i1]+1
  2. 找到一个位置 j < i j < i j<i,使得 a [ j ] ≠ a [ i ] a[j] \neq a[i] a[j]=a[i],将区间 [ j + 1 , i ] [j+1, i] [j+1,i] 内的灯泡全部点亮,此时操作次数为 f [ j ] + 1 f[j] + 1 f[j]+1

综合以上两种情况,我们可以得到状态转移方程:

f [ i ] = min ⁡ { f [ i − 1 ] + 1 , min ⁡ j < i , a [ j ] ≠ a [ i ] { f [ j ] + 1 } } f[i] = \min\{f[i-1] + 1, \min_{j < i, a[j] \neq a[i]}\{f[j] + 1\}\} f[i]=min{f[i1]+1,j<i,a[j]=a[i]min{f[j]+1}}

最终答案即为 f [ n ] f[n] f[n]

为了优化第二种情况的转移,我们可以用一个哈希表来维护每种颜色最后一次出现的位置对应的最小操作次数。这样可以将时间复杂度降低到 O ( n ) O(n) O(n)

参考代码

  • Python
t = int(input())
for _ in range(t):
    n = int(input())
    a = list(map(int, input().split()))
    if a[0] != a[-1]:
        print(1)
        continue
    f = [0] * (n + 1)
    m = {a[0]: 0}
    for i in range(1, n + 1):
        f[i] = 1 + f[i - 1]
        for v, min_f in m.items():
            if v != a[i - 1]:
                f[i] = min(f[i], 1 + min_f)
        if a[i - 1] not in m or f[i - 1] < m[a[i - 1]]:
            m[a[i - 1]] = f[i - 1]
    print(f[n])

  • Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        while (t-- > 0) {
            int n = scanner.nextInt();
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = scanner.nextInt();
            }
            if (a[0] != a[n - 1]) {
                System.out.println(1);
                continue;
            }
            int[] f = new int[n + 1];
            Map<Integer, Integer> m = new HashMap<>();
            m.put(a[0], 0);
            for (int i = 1; i <= n; i++) {
                f[i] = 1 + f[i - 1];
                for (Map.Entry<Integer, Integer> entry : m.entrySet()) {
                    int v = entry.getKey();
                    int min_f = entry.getValue();
                    if (v != a[i - 1]) {
                        f[i] = Math.min(f[i], 1 + min_f);
                    }
                }
                if (!m.containsKey(a[i - 1]) || f[i - 1] < m.get(a[i - 1])) {
                    m.put(a[i - 1], f[i - 1]);
                }
            }
            System.out.println(f[n]);
        }
    }
}

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

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        if (a[0] != a[n - 1]) {
            cout << 1 << endl;
            continue;
        }
        vector<int> f(n + 1, 0);
        map<int, int> m;
        m[a[0]] = 0;
        for (int i = 1; i <= n; ++i) {
            f[i] = 1 + f[i - 1];
            for (auto &[v, min_f] : m) {
                if (v != a[i - 1]) {
                    f[i] = min(f[i], 1 + min_f);
                }
            }
            if (m.find(a[i - 1]) == m.end() || f[i - 1] < m[a[i - 1]]) {
                m[a[i - 1]] = f[i - 1];
            }
        }
        cout << f[n] << endl;
    }
    return 0;
}

03.K小姐的区间问题

问题描述

K小姐有一个长度为 n n n 的数组 n u m s nums nums,她定义函数 f ( l e f t , r i g h t , v a l ) f(left, right, val) f(left,right,val) 表示在区间 [ l e f t , r i g h t ] [left, right] [left,right] 内数字 v a l val val 出现的次数。

现在K小姐想知道有多少对 ( i , j ) (i, j) (i,j) 满足 i < j i < j i<j f ( 1 , i , n u m s [ i ] ) > f ( j , n , n u m s [ j ] ) f(1, i, nums[i]) > f(j, n, nums[j]) f(1,i,nums[i])>f(j,n,nums[j])

输入格式

第一行包含一个正整数 n n n,表示数组 n u m s nums nums 的长度。

第二行包含 n n n 个用空格分开的整数 n u m s [ 1 ] , n u m s [ 2 ] , … , n u m s [ n ] nums[1], nums[2], \dots, nums[n] nums[1],nums[2],,nums[n],表示数组 n u m s nums nums 的元素。

输出格式

输出一个整数,表示满足条件的 ( i , j ) (i, j) (i,j) 对数。

样例输入

6
1 2 1 2 2 1

样例输出

5

样例说明

存在以下五对 ( i , j ) (i, j) (i,j) 满足条件: ( 3 , 5 ) (3, 5) (3,5), ( 3 , 6 ) (3, 6) (3,6), ( 4 , 5 ) (4, 5) (4,5), ( 4 , 6 ) (4, 6) (4,6), ( 5 , 6 ) (5, 6) (5,6)

数据范围

  • 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105
  • 1 ≤ n u m s [ i ] ≤ 1 0 9 1 \leq nums[i] \leq 10^9 1nums[i]109

题解

我们可以使用树状数组和哈希表来优化。具体做法如下:

  1. 从后往前遍历数组 n u m s nums nums,用哈希表 c o u n t e r R i g h t counterRight counterRight 统计每个数字在区间 [ j , n ] [j, n] [j,n] 内出现的次数,同时将这些次数信息插入树状数组中。
  2. 从前往后遍历数组 n u m s nums nums,对于每个位置 i i i,我们需要查询在区间 [ i + 1 , n ] [i+1, n] [i+1,n] 内有多少个数字 v a l val val 满足 f ( i + 1 , n , v a l ) < f ( 1 , i , n u m s [ i ] ) f(i+1, n, val) < f(1, i, nums[i]) f(i+1,n,val)<f(1,i,nums[i])。这可以通过查询树状数组中小于 c o u n t e r L e f t [ n u m s [ i ] ] counterLeft[nums[i]] counterLeft[nums[i]] 的前缀和来实现。
  3. 在统计完 i i i 位置后,我们需要将 c o u n t e r R i g h t [ n u m s [ i + 1 ] ] counterRight[nums[i+1]] counterRight[nums[i+1]] 从树状数组中删除,以保证后续查询的正确性。

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

参考代码

  • Python
from collections import defaultdict

class FenwickTree:
    def __init__(self, n):
        self.MXN = n + 2
        self.tree = [0] * self.MXN

    def lowbit(self, x):
        return x & (-x)


    def update(self, index, x):
        i = index + 1
        while i < self.MXN:
            self.tree[i] += x
            i += self.lowbit(i)

   
    def queryPre(self, n):
        ans = 0
        while n:
            ans += self.tree[n]
            n -= self.lowbit(n)
        return ans

   
    def query(self, a, b):
        return self.queryPre(b + 1) - self.queryPre(a)

def solution():
    n = int(input())
    nums = list(map(int, input().split()))
    fenwick_tree = FenwickTree(n)
    counterRight = defaultdict(int)
    for i in range(n - 1, 1, -1):
        counterRight[nums[i]] += 1
        fenwick_tree.update(counterRight[nums[i]], 1)

    ans = 0
    counterLeft = defaultdict(int)
    counterLeft[nums[0]] += 1
    for i, val in enumerate(nums[1:n-1],1):
        counterLeft[val] += 1
        ans += fenwick_tree.queryPre(counterLeft[val])
        fenwick_tree.update(counterRight[nums[i + 1]], -1)
        counterRight[nums[i + 1]] -= 1

    print(ans)

if __name__ == "__main__":
    solution()

  • Java
import java.util.*;

class FenwickTree {
    private int MXN;
    private int[] tree;

    private int lowbit(int x) {
        return x & (-x);
    }

    public FenwickTree(int n) {
        MXN = n + 2;
        tree = new int[MXN];
    }

    public void update(int index, int x) {
        int i = index + 1;
        while (i < MXN) {
            tree[i] += x;
            i += lowbit(i);
        }
    }

    public int queryPre(int n) {
        int ans = 0;
        while (n > 0) {
            ans += tree[n];
            n -= lowbit(n);
        }
        return ans;
    }

    public int query(int a, int b) {
        return queryPre(b + 1) - queryPre(a);
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = scanner.nextInt();
        }
        FenwickTree fenwickTree = new FenwickTree(n);
        Map<Integer, Integer> counterRight = new HashMap<>();
        for (int i = n - 1; i > 1; i--) {
            counterRight.put(nums[i], counterRight.getOrDefault(nums[i], 0) + 1);
            fenwickTree.update(counterRight.get(nums[i]), 1);
        }

        int ans = 0;
        Map<Integer, Integer> counterLeft = new HashMap<>();
        counterLeft.put(nums[0], 1);
        for (int i = 1; i < n - 1; i++) {
            int val = nums[i];
            counterLeft.put(val, counterLeft.getOrDefault(val, 0) + 1);
            ans += fenwickTree.queryPre(counterLeft.get(val));
            fenwickTree.update(counterRight.get(nums[i + 1]), -1);
            counterRight.put(nums[i + 1], counterRight.get(nums[i + 1]) - 1);
        }

        System.out.println(ans);
    }
}

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

class FenwickTree {
private:
    int MXN;
    vector<int> tree;

    int lowbit(int x) {
        return x & (-x);
    }

public:
    FenwickTree(int n) : MXN(n + 2), tree(n + 2, 0) {}

    void update(int index, int x) {
        int i = index + 1;
        while (i < MXN) {
            tree[i] += x;
            i += lowbit(i);
        }
    }

    int queryPre(int n) {
        int ans = 0;
        while (n) {
            ans += tree[n];
            n -= lowbit(n);
        }
        return ans;
    }

    int query(int a, int b) {
        return queryPre(b + 1) - queryPre(a);
    }
};

void solution() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    FenwickTree fenwick_tree(n);
    unordered_map<int, int> counterRight;
    for (int i = n - 1; i > 1; i--) {
        counterRight[nums[i]]++;
        fenwick_tree.update(counterRight[nums[i]], 1);
    }

    int ans = 0;
    unordered_map<int, int> counterLeft;
    counterLeft[nums[0]]++;
    for (int i = 1; i < n - 1; i++) {
        int val = nums[i];
        counterLeft[val]++;
        ans += fenwick_tree.queryPre(counterLeft[val]);
        fenwick_tree.update(counterRight[nums[i + 1]], -1);
        counterRight[nums[i + 1]]--;
    }

    cout << ans << endl;
}

int main() {
    solution();
    return 0;
}

写在最后

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

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值