刚做完阿里云的算法题,汗流浃背!

文摘   2024-09-04 11:31   广东  

通知:代码随想录算法训练营 46期在下周三(9月11日)开营,目前可以报名,提前拉群坐等开营。

上周四(8月29日 )晚上卡码网举办了三十二期周赛(阿里云23年笔试真题)。

阿里云这次算法题,堪称贪心大杂烩,都是贪心题。

23年各大厂笔试题,大家可以在卡码网(https://kamacoder.com/contest.php)去练习

题目的评论区有各个语言的版本,也有很多录友分享的解题思路,都很不错。

以下为阿里云23年笔试算法题,解题报告:(其他语言版本,可以看题目链接评论区)

优秀数组

题目链接:https://kamacoder.com/problempage.php?pid=1241

解题思路

1、初始分析

  • 给定一个排列 p,我们首先构建一个 pos 数组,使得 pos[i] 表示 i 在排列 p 中的位置。
  • 我们需要判断数组 a 是否是一个优秀数组,即 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 对于所有 i 都成立。
  • 我们的目标是通过最少的相邻元素交换,使得数组 a 不再是一个优秀数组。

2、思路

  • 要使数组 a 不再是优秀数组,我们只需要打破条件 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 中的某一个。
  • 一种简单的做法是让 pos[a[i]]pos[a[i+1]] 之间的距离超过 d,或者直接让 pos[a[i]] >= pos[a[i+1]]

3、具体方法

  • 只需要考虑 a 中相邻元素的顺序,并判断如何交换 p 中相邻元素使得其顺序被打破。
  • 假设我们需要在 p 中交换某些元素来实现上述目标,那么最小的交换次数是将 a[i]a[i+1] 的位置交换。
  • 如果 pos[a[i]] + 1 == pos[a[i+1]],则需要一步交换。

4、特别情况

  • 还需要考虑,如果通过交换相邻元素无法解决问题的情况。比如 pos[a[i+1]] 的位置无法移到 pos[a[i]] 的前面或超过 d

C++代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>

using namespace std;

int main() {
    int n, m, d;
    cin >> n >> m >> d;

    vector<intp(n + 1);
    vector<intpos(n + 1);

    // 读取排列 p,并构建位置数组 pos
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
        pos[p[i]] = i;
    }

    vector<inta(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i];
    }

    int min_operations = INT_MAX;

    // 遍历数组 a 的相邻元素
    for (int i = 0; i < m - 1; i++) {
        int current_pos = pos[a[i]];
        int next_pos = pos[a[i + 1]];

        // 检查 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 是否成立
        if (current_pos < next_pos && next_pos <= current_pos + d) {
            // 计算需要的最少操作次数
            int distance = next_pos - current_pos;

            // Case 1: 交换 current_pos 和 next_pos
            min_operations = min(min_operations, distance);

            // Case 2: 如果 next_pos + d <= n,考虑使 pos[a[i+1]] 超过 pos[a[i]] + d
            if (current_pos + d + 1 <= n) {
                min_operations = min(min_operations, d + 1 - distance);
            }
        } else {
            min_operations = 0;
        }
    }

    cout << min_operations << endl;
    return 0;
}

时间复杂度为 O(m)

升序数组

题目链接:https://kamacoder.com/problempage.php?pid=1241

解题思路

贪心思路

  • 计算相邻元素差值

    • 对于数组 a,计算每对相邻元素的差值 diff[i] = a[i+1] - a[i]
    • 如果 diff[i] 为负数,意味着 a[i+1]a[i] 小或相等,需要通过操作使 a[i+1] 变大。
  • 确定最小操作次数

    • 计算所有相邻元素中的最小差值 minDifference,即 minDifference = min(diff[i])
    • 如果 minDifference 为负数或零,则需要进行 -minDifference + 1 次操作,使得 a[i+1] 大于 a[i],从而使数组严格递增。
  • 实现细节

    • 遍历数组的每对相邻元素,找出最小的差值。
    • 根据最小差值,计算出最少的操作次数。
#include <iostream>
#include <vector>
#include <climits>

using namespace std;

int main()
{
    int n;
    cin >> n;

    vector<intarr(n);  // 用于存储输入数组
    vector<int> differences;  // 用于存储相邻元素的差值

    for(int i = 0; i < n; i++) {
        cin >> arr[i];
        if(i > 0) differences.push_back(arr[i] - arr[i - 1]);

    }

    int minDifference = INT_MAX;

    // 寻找最小的差值
    for(int diff : differences) {
        if(diff < minDifference) {
            minDifference = diff;
        }
    }

    // 如果最小差值是负数或零,计算所需的操作次数
    int minOperations = max(0, -minDifference + 1);

    cout << minOperations << endl;

    return 0;
}
 

最大字典序无重复串

题目链接:https://kamacoder.com/problempage.php?pid=1243

解题思路

贪心思路

为了保证字典序最大,我们优先放置字母 b,然后再放置字母 a。在放置字符时,我们还需注意不能超过连续 k 次相同字符:

  • 如果当前已经连续放置了 k 次相同字符,必须切换到另一个字符。
  • 每次放置字符后,相应的字符数量减少,同时更新当前字符的连续计数。

实现步骤:

  • 初始化:根据输入的 x, y, k 值,检查是否有可能构造出满足条件的字符串。初始化结果字符串的大小,并设置初始计数器。
  • 循环放置字符
    • 优先放置字符 b,如果 b 的数量已经足够,或者已经放置了 k 次字符 b,则放置字符 a
    • 如果已经放置了 k 次相同字符,则强制切换到另一个字符。

C++代码如下:

#include<iostream>
#include<string>
using namespace std;

int main() {
    int countA, countB, maxRepeat;
    cin >> countA >> countB >> maxRepeat;

    // 检查是否有可能生成满足条件的字符串
    if (countA > (countB + 1) * maxRepeat || countB > (countA + 1) * maxRepeat) {
        cout << -1 << endl;
        return 0;
    }

    string result(countA + countB, ' ');  // 预先分配字符串大小
    int currentA = 0, currentB = 0;       // 当前连续 'a' 和 'b' 的计数
    int pos = 0;                          // 当前填充位置

    while (countA > 0 || countB > 0) {
        // 当可以继续添加 'a' 或 'b' 且没有超过最大连续限制时
        if (currentA < maxRepeat && currentB < maxRepeat) {
            if (countA <= countB * maxRepeat) {
                result[pos++] = 'b';
                countB--;
                currentB++;
                currentA = 0;
            } else {
                result[pos++] = 'a';
                countA--;
                currentA++;
                currentB = 0;
            }
        }

        // 当当前字符达到最大连续限制时,切换到另一个字符
        if (currentA == maxRepeat || currentB == maxRepeat) {
            if (result[pos - 1] == 'a') {
                result[pos++] = 'b';
                countB--;
                currentB = 1;
                currentA = 0;
            } else {
                result[pos++] = 'a';
                countA--;
                currentA = 1;
                currentB = 0;
            }
        }
    }

    cout << result << endl;
    return 0;
}

时间复杂度:O(n)


代码随想录
认准代码随想录,学习算法不迷路。 刷题网站:programmercarl.com
 最新文章