• OPPO 后端二面,凉凉。。。


    美众议院通过 TikTok 法案

    之前我们讲了 老美要求字节跳动在 165 天内剥离短视频应用 TikTok,当时的最新进度是 TikTok 给 1.7 亿美国用户发弹窗,发动用户群众给国会打电话进行抗议。

    但显然这点力度的抗议并不会造成什么实质影响。

    昨晚,美国众议院的议员们正式投票通过了该法案(H.R.7521),之后的流程还需要得到美国参议院的通过,然后才是提交给总统拜登批准。

    美国众议院的威斯康星州共和党众议员迈克·加拉格尔(右)是一项针对TikTok法案的主要支持者之一
    美国众议院的威斯康星州共和党众议员迈克·加拉格尔(右)是一项针对TikTok法案的主要支持者之一

    看似流程还长,但大概率不会出现什么变数,毕竟针对 TikTok 是两党的少数共识。

    在正式投票之前,白宫秘书就公开称赞该提案,称拜登政府"希望看到这项法案得以通过,这样它就能被送到总统的办公桌上"。

    这事儿如果真的被美国得逞,真的是很坏的示范。

    现在比较合理的破局方式,只能是期望当时躲过特朗普狙击的方法能再奏效一次。

    希望会有一些线下的抗议活动,动静越大越好,尽量拖延法案通过的日期。

    只要法案通过日期延后,再加上法案生效后还有 165 天时间,就有可能避开美国大选,到时如果发生新政交接,或许就能再次获得喘息机会。

    ...

    回归主线。

    真心希望 TikTok 不会原地变外企,先不做字节跳动相关题目了。

    来看一道 OPPO 二面算法原题。

    蓝厂的花边新闻虽然不多,但一直是低调赚大钱的代表之一。

    这次二面出的算法题水平也不错。

    相比原题,题面稍有修改,但数据范围和解法完全一致。

    题目描述

    平台:LeetCode

    题号:864

    给定一个二维网格 g,其中:

    • '.' 代表一个空房间
    • '#' 代表一堵墙
    • '@' 是起点
    • 小写字母代表钥匙
    • 大写字母代表锁

    我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。

    我们不能在网格外面行走,也无法穿过一堵墙。

    如果途经一个钥匙,我们就把它捡起来,除非我们手里有对应的钥匙,否则无法通过锁。

    假设 k 为 钥匙/锁 的个数,且满足  ,字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母。

    换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。

    返回获取所有钥匙所需要的移动的最少次数。

    如果无法获取所有钥匙,返回 -1 。

    示例 1: alt

    输入:g = ["@.a.#","###.#","b.A.B"]

    输出:8

    解释:目标是获得所有钥匙,而不是打开所有锁。
    • 1

    示例 2: alt

    输入:g = ["@..aA","..B#.","....b"]

    输出:6
    • 1

    示例 3: alt

    输入: g = ["@Aa"]

    输出: -1
    • 1

    提示:

    • g[i][j] 只含有  '.', '#''@''a'-'f' 以及  'A'-'F'
    • 钥匙的数目范围是 
    • 每个钥匙都对应一个不同的字母
    • 每个钥匙正好打开一个对应的锁

    BFS + 状态压缩

    一道常规的 BFS 运用题,只不过需要在 BFS 过程中记录收集到的钥匙状态。

    利用「钥匙数量不超过 ,并按字母顺序排列」,我们可以使用一个 int 类型二进制数 state 来代指当前收集到钥匙情况:

    • state 的二进制中的第 位为 1,代表当前种类编号为 的钥匙 「已被收集」,后续移动若遇到对应的锁则 「能通过」
    • state 的二进制中的第 位为 0,代表当前种类编号为 的钥匙 「未被收集」,后续移动若遇到对应的锁则 「无法通过」

    其中「钥匙种类编号」则按照小写字母先后顺序,从 开始进行划分对应:即字符为 a 的钥匙编号为 0,字符为 b 的钥匙编号为 1,字符为 c 的钥匙编号为 2 ...

    当使用了这样的「状态压缩」技巧后,我们可以很方便通过「位运算」进行 「钥匙检测」「更新钥匙收集状态」

    • 钥匙检测: (state >> k) & 1,若返回 1 说明第 位为 1,当前持有种类编号为 k 的钥匙
    • 更新钥匙收集状态: state |= 1 << k,将 state 的第 位设置为 1,代表当前新收集到种类编号为 k 的钥匙

    搞明白如何记录当前收集到的钥匙状态后,剩下的则是常规 BFS 过程:

    1. 起始遍历一次棋盘,找到起点位置,并将其进行入队,队列维护的是 三元组状态(其中 代表当前所在的棋盘位置, 代表当前的钥匙收集情况) 同时统计整个棋盘所包含的钥匙数量 cnt,并使用 数组/哈希表 记录到达每个状态所需要消耗的最小步数 step

    2. 进行四联通方向的 BFS,转移过程中需要注意「遇到锁时,必须有对应钥匙才能通过」&「遇到钥匙时,需要更新对应的 state 再进行入队」

    3. BFS 过程中遇到 state = (1 << cnt) - 1 时,代表所有钥匙均被收集完成,可结束搜索

    Java 代码:

    class Solution {
        static int N = 35, K = 10, INF = 0x3f3f3f3f;
        static int[][][] dist = new int[N][N][1 << K];
        static int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
        public int shortestPathAllKeys(String[] g) {
            int n = g.length, m = g[0].length(), cnt = 0;
            Deque<int[]> d = new ArrayDeque<>();
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    Arrays.fill(dist[i][j], INF);
                    char c = g[i].charAt(j);
                    if (c == '@') {
                        d.addLast(new int[]{i, j, 0});
                        dist[i][j][0] = 0;
                    } else if (c >= 'a' && c <= 'z') cnt++;
                }
            }
            while (!d.isEmpty()) {
                int[] info = d.pollFirst();
                int x = info[0], y = info[1], cur = info[2], step = dist[x][y][cur];
                for (int[] di : dirs) {
                    int nx = x + di[0], ny = y + di[1];
                    if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                    char c = g[nx].charAt(ny);
                    if (c == '#'continue;
                    if ((c >= 'A' && c <= 'Z') && (cur >> (c - 'A') & 1) == 0continue;
                    int ncur = cur;
                    if (c >= 'a' && c <= 'z') ncur |= 1 << (c - 'a');
                    if (ncur == (1 << cnt) - 1return step + 1;
                    if (step + 1 >= dist[nx][ny][ncur]) continue;
                    dist[nx][ny][ncur] = step + 1;
                    d.addLast(new int[]{nx, ny, ncur});
                }
            }
            return -1;
        }
    }
    • 1

    C++ 代码:

    class Solution {
        int N = 35, K = 10, INF = 0x3f3f3f3f;
        vector<vector<vector<int>>> dist = vector<vector<vector<int>>>(N, vector<vector<int>>(N, vector<int>(1<    vector<vector<int>> dirs = {{1,0}, {-1,0}, {0,1}, {0,-1}};
    public:
        int shortestPathAllKeys(vector<string>& g) {
            int n = g.size(), m = g[0].size(), cnt = 0;
            queue<vector<int>> d;
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    fill(dist[i][j].begin(), dist[i][j].end(), INF);
                    char c = g[i][j];
                    if (c == '@') {
                        d.push({i, j, 0});
                        dist[i][j][0] = 0;
                    } else if (c >= 'a' && c <= 'z') cnt++;
                }
            }
            while (!d.empty()) {
                vector<int> info = d.front();
                d.pop();
                int x = info[0], y = info[1], cur = info[2], step = dist[x][y][cur];
                for (vector<int> di : dirs) {
                    int nx = x + di[0], ny = y + di[1];
                    if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                    char c = g[nx][ny];
                    if (c == '#'continue;
                    if ((c >= 'A' && c <= 'Z') && (cur >> (c - 'A') & 1) == 0continue;
                    int ncur = cur;
                    if (c >= 'a' && c <= 'z') ncur |= 1 << (c - 'a');
                    if (ncur == (1 << cnt) - 1return step + 1;
                    if (step + 1 >= dist[nx][ny][ncur]) continue;
                    dist[nx][ny][ncur] = step + 1;
                    d.push({nx, ny, ncur});
                }
            }
            return -1;
        }
    };
    • 1

    Python3 代码:

    class Solution:
        def shortestPathAllKeys(self, g: List[str]) -> int:
            dirs = [[0,1], [0,-1], [1,0], [-1,0]]
            n, m, cnt = len(g), len(g[0]), 0
            dist = defaultdict(lambda : 0x3f3f3f3f)
            for i in range(n):
                for j in range(m):
                    c = g[i][j]
                    if c == '@':
                        d = deque([(i, j, 0)])
                        dist[(i, j, 0)] = 0
                    elif 'a' <= c <= 'z':
                        cnt += 1
            while d:
                x, y, cur = d.popleft()
                step = dist[(x, y, cur)]
                for di in dirs:
                    nx, ny = x + di[0], y + di[1]
                    if nx < 0 or nx >= n or ny < 0 or ny >= m:
                        continue
                    c = g[nx][ny]
                    if c == '#':
                        continue
                    if 'A' <= c <= 'Z' and (cur >> (ord(c) - ord('A')) & 1) == 0:
                        continue
                    ncur = cur
                    if 'a' <= c <= 'z':
                        ncur |= (1 << (ord(c) - ord('a')))
                    if ncur == (1 << cnt) - 1:
                        return step + 1
                    if step + 1 >= dist[(nx, ny, ncur)]:
                        continue
                    dist[(nx, ny, ncur)] = step + 1
                    d.append((nx, ny, ncur))
            return -1
    • 1

    TypeScript 代码:

    function shortestPathAllKeys(g: string[]): number {
        const dirs = [[1,0],[-1,0],[0,1],[0,-1]]
        let n = g.length, m = g[0].length, cnt = 0
        const dist = new Array<Array<Array<number>>>()
        for (let i = 0; i < n; i++) {
            dist[i] = new Array<Array<number>>(m)
            for (let j = 0; j < m; j++) {
                dist[i][j] = new Array<number>(1 << 10).fill(0x3f3f3f3f)
            }
        }
        const d = []
        for (let i = 0; i < n; i++) {
            for (let j = 0; j < m; j++) {
                if (g[i][j] == '@') {
                    d.push([i, j, 0]); dist[i][j][0] = 0
                } else if (g[i][j] >= 'a' && g[i][j] <= 'z') cnt++
            }
        }
        while (d.length > 0) {
            const info = d.shift()
            const x = info[0], y = info[1], cur = info[2], step = dist[x][y][cur]
            for (const di of dirs) {
                const nx = x + di[0], ny = y + di[1]
                if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue
                const c = g[nx][ny]
                if (c == '#'continue
                if ('A' <= c && c <= 'Z' && ((cur >> (c.charCodeAt(0) - 'A'.charCodeAt(0)) & 1) == 0)) continue
                let ncur = cur
                if ('a' <= c && c <= 'z') ncur |= 1 << (c.charCodeAt(0) - 'a'.charCodeAt(0))
                if (ncur == (1 << cnt) - 1return step + 1
                if (step + 1 >= dist[nx][ny][ncur]) continue
                d.push([nx, ny, ncur])
                dist[nx][ny][ncur] = step + 1
            }
        }
        return -1
    }
    • 1
    • 时间复杂度:
    • 空间复杂度:

    我是宫水三叶,每天都会分享算法知识,并和大家聊聊近期的所见所闻。

    欢迎关注,明天见。

    更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

  • 相关阅读:
    一些RCE的汇总
    Python读取mongodb数据库
    【Unity】预制体材质变(Clone)克隆体问题
    CAD绘制零件平面图
    《致新来的你》
    5次折戟IPO,互联网家装这条路,没土巴兔想的那么简单
    查题api对接
    vue+Fullcalendar
    Vue利用flex布局实现TV端城市列表
    c++模板库容器list vector map set操作和性能对比
  • 原文地址:https://blog.csdn.net/weixin_33243821/article/details/136711232