快手2020校园招聘秋招笔试--工程C试卷

病毒检测

题目描述

小明最近在做病毒自动检测,他发现,在某些 \(library\) 的代码段的二进制表示中,如果包含子串并且恰好有 \(k\)\(1\),就有可能有潜在的病毒。\(library\) 的二进制表示可能很大,并且子串可能很多,人工分析不可能,于是他想写个程序来先算算到底有多少个子串满足条件。如果子串内容相同,但是开始或者结束位置不一样,则被认为是不同的子串。

注:子串一定是连续的。例如"010"有 \(6\) 个子串,分别是 "0, "1", "0", "01", "10", "010"。

数据范围

  • \(0 \leq k \leq 10^6\)
  • \(1 \leq S.length \leq 10^6\)

解题思路

有如下结论成立

  • \(A\) = 包含第 \(i\) 到第 \(i+k-1\)\(1\) 且仅含 \(k\)\(1\) 的字符串的数量。
  • \(B\) = 第 \(i-1\) 到第 \(i\)\(1\) 之间 \(0\) 的数量。
  • \(C\) = 第 \(i+k-1\) 到第 \(i+k\)\(1\) 之间 \(0\) 的数量。
  • \(A= (B+1) \times (C+1)\)

故,只需预处理出第 \(i\) 到 第 \(i+1\)\(1\) 之间 \(0\) 的个数,便可很容易得到仅包含 \(k\)\(1\) 的子串的数量。

代码实现

long long solve(string s, int k) {
    typedef long long ll;
    int n = s.length();
    int idx[n + 1], m = 0;
    // 预处理出所有1的下标
    // 通过“相邻”两个1下标得到这两个1之间的0的个数
    for (int i = 1; i <= n; i++)
        if (s[i - 1] == '1')idx[++m] = i;
    idx[0] = 0, idx[m + 1] = n + 1;
    ll res = 0;
    // k为0时特殊处理
    if (k == 0) {
        for (int i = 1; i <= m + 1; i++) {
            ll x = idx[i] - idx[i - 1] - 1;
            res += (x + 1) * x / 2;
        }
    } else {
        for (int l = 1, r = l + k - 1; r <= m; l++, r++)
            res += (idx[l] - idx[l - 1]) * (idx[r + 1] - idx[r]);
    }
    return res;
}

时间复杂度:\(O(n)\)

空间复杂度:\(O(n)\)

优化实现

  1. “相邻”两个 \(1\) 之间 \(0\) 的个数可以在计算结果时边统计。
  2. 在计算包含第 \(i\) 到第 \(i+k-1\)\(1\) 且仅含 \(k\)\(1\) 的字符串的数量时,仅需知道第 \(i-1\) 到第 \(i\)\(1\) 之间 \(0\) 的数量。
  3. \(dp[i]\) = 第 \(i\) 到第 \(i+1\)\(1\) 之间 \(0\) 的个数,\(dp\) 的大小只需为 \(k+1\)
long long solve(string s, int k) {
    typedef long long ll;
    ll res = 0;
    int dp[k + 1], m = 0;
    dp[0] = 1;
    for (char c: s) {
        if (c == '1')m++, dp[m % (k + 1)] = 0;
        if (m >= k)res += dp[(m - k) % (k + 1)];
        // 统计第m到第m+1个1之间0的数量(数量含第m个1本身)
        dp[m % (k + 1)]++;
    }
    return res;
}

时间复杂度:\(O(n)\)

空间复杂度:\(O(k)\)

考试成绩

题目描述

现在你的班级刚刚参加了一个只有单选题的考试。班级一共 \(n\) 个学生,考试有 \(m\) 个问题。每个题目都有 \(5\) 个可选答案(\(A\)\(B\)\(C\)\(D\)\(E\))。并且每个题目只有一个正确答案。每个题目的分数并不一样,第 \(i\) 个题目的分数用 \(a_i\) 表示。如果题目没答对该题会获得 \(0\) 分。
考试结束后,每个学生都记得自己的答案,但是他们还不知道正确答案是什么。如果非常乐观的考虑,他们班级最多可能得到多少分呢?

数据范围

  • \(1 \leq n \leq 10^3\)
  • \(1 \leq m \leq 10^3\)
  • \(1 \leq a_i \leq 10^9\)

解题思路

对于每道题,将选择人数最多的选项作为答案即可。

代码实现

/**
 * @param ps 每位同学每道题的答案
 * @param a 每道题的分数
 */
long long solve(vector<string> ps, vector<int> a) {
    int m = a.size(), cnt[m + 1][5];
    memset(cnt, 0, sizeof cnt);
    for (auto &s: ps)
        for (int i = 0; i < m; i++)
            cnt[i][s[i] - 'A']++;
    long long res = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 1; j < 5; j++)
            if (cnt[i][j] > cnt[i][0])cnt[i][0] = cnt[i][j];
        res += a[i] * cnt[i][0];
    }
    return res;
}

时间复杂度:\(O(mn)\)

空间复杂度:\(O(m)\)

石头碰撞

题目描述

给定一组石头,每个石头有一个正数的重量。每一轮开始的时候,选择两个石头一起碰撞,假定两个石头的重量为 \(x,y\)\(x \leq y\),碰撞结果为

  1. 如果 \(x==y\),碰撞结果为两个石头消失。
  2. 如果 \(x != y\),碰撞结果两个石头消失,生成一个新的石头,新石头重量为 \(y-x\)

最终最多剩下一个石头为结束,问最小的剩余石头质量的可能是多少?

数据范围

第一行输入石头个数 \(n\)\(1 \leq n \leq 100\)

第二行输入石头质量,以空格分割,石头质量总和为 \(sum\),$1 \leq sum \leq 10^4 $

解题思路

转换为 \(0-1\) 背包问题,等价于,从 \(n\) 个石头中选,在重量之和不超过 \(n\) 个石头总重量一半的前提下,所能选取的石头的重量之和的最大值。

代码实现

int main() {
    int n, sum = 0;
    scanf("%d", &n);
    int a[n + 1];
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    int k = sum >> 1;
    int dp[k + 1];
    memset(dp, 0, sizeof dp);
    for (int i = 1; i <= n; i++) {
        for (int j = k; j >= a[i]; j--) {
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
        }
    }
    printf("%d", sum - (dp[k] << 1));
    return 0;
}

时间复杂度:\(O(n \times sum)\)

空间复杂度:\(O(sum)\)

蓄水池大作战

题目描述

在你面前有 \(n\) 个蓄水池,他们组成了树形结构(由 \(n-1\) 条边连接)。蓄水池节点编号从 \(1\) 开始到 \(n\)。对每个蓄水池节点来说,他的儿子蓄水池节点都摆放在他的下面,并且和它用水管相连,根据重力,水会向下流动。现在我们要在蓄水池上做一些操作:

  1. 把节点 \(v\) 填满水。然后v的所有儿子节点水也会被填满
  2. 清空节点 \(v\) 的水。然后v所有的父亲节点水都会被清空
  3. 询问每个蓄水池节点是否有水。

初始状态时候,每个节点都是空的。

现在我们会依次进行一系列操作,我们想提前知道每次操作后的结果,你能帮忙解决吗?

输入描述

第一行包含一个正整数 \(n(1 \leq n \leq 1000)\),表示蓄水池节点的数量。

后面 \(n-1\) 行,每行有两个数字 \(a[i], b[i]\)\(1 \leq a[i], b[i] \leq n, a[i] \neq b[i]\)),表示蓄水池的连接关系。

接下来的一行包含一个整数 \(q(1 \leq q \leq 1000)\),表示我们要进行的操作的数量。

最后的 \(q\) 行中,每行包含两个数字 \(c[i],v[i]\)\(1 \leq c[i] \leq 3,1 \leq v[i] \leq n\))。其中 \(c[i]\) 表示操作类型(\(1\),\(2\) 或者\(3\))。\(v[i]\) 表示操作对应的蓄水池节点。

输入数据保证合理,是一个连通的树。

提示:对于 \(a[i], b[i]\),默认小节点是大节点的父节点

输出描述

对于每个操作 \(3\)\(c[i] = 3\)),输出一个数字 \(1\)或者 \(0\)\(1\) 表示 \(v[i]\) 蓄水池节点有水,\(0\) 表示没水。

解题思路

模拟即可

代码实现

void dfs(map<int, vector<int>> &mp, vector<int> &v, int x, int m) {
    v[x] = m;
    for (int i = 0; i < mp[x].size(); i++)
        dfs(mp, v, mp[x][i], m);
}

int main() {
    int n, a, b, q;
    scanf("%d", &n);
    map<int, vector<int>> son, pat;
    vector<int> v(n + 1, 0);
    while (--n) {
        scanf("%d%d", &a, &b);
        if (a > b) swap(a, b);
        son[a].push_back(b);
        pat[b].push_back(a);
    }
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d", &a, &b);
        if (a == 1) dfs(son, v, b, 1);
        else if (a == 2) dfs(pat, v, b, 0);
        else printf("%d\n", v[b]);
    }
    return 0;
}

时间复杂度:\(O(qn)\)

空间复杂度:\(O(n)\)

END

题目来源:快手2020校园招聘秋招笔试--工程C试卷

文章声明:题目来源 牛客 平台,如有侵权,请联系删除!

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。

posted @ 2024-06-23 13:47  字节幺零二四  阅读(45)  评论(0)    收藏  举报