• 单周赛 251 题解


    最后一题真考验人的码力,这种大数据结构题,一定要想清楚思路之后再实现,实现完将大大加强你的自信!

    知识点:数位和,贪心,全排列,海明距离,深度优先搜索,树序列化

    字符串转化后的各位数字之和

    给定一个字符串 (s),每一位都是一个小写字母

    现在将小写字母替换成数字,即 a = 1, b = 2, ...,计算数位和得到新数字,做 (k) 轮,返回最终的答案

    题解

    模拟

    class Solution {
    public:
        int getSum(int x)
        {
            int ans = 0;
            while (x) {
                ans += x % 10;
                x /= 10;
            }
            return ans;
        }
        int getLucky(string s, int k) {
            int ans = 0;
            for (int i = 0; i < s.length(); ++i) {
                ans += getSum(s[i] - 'a' + 1);
            }
            k--;
            for (int i = 0; i < k; ++i) {
                ans = getSum(ans);
            }
            return ans;
        }
    };
    

    子字符串突变后可能得到的最大整数

    给定一个字符串 (s),每一位都是一个 (0-9) 的数字

    给定一个数组 (w),你可以将 (s) 中的数字 (i) 映射成 (w[i])

    现在可以对 (s) 的任意子字符串进行映射,返回可能的最大整数

    题解

    贪心,遍历数组

    • 如果当前数字映射之后比映射前大,那么完成映射
    • 如果比之前小
      • 如果还没有开始映射,继续遍历
      • 如果已经开始映射了,退出循环
    class Solution {
    public:
        string maximumNumber(string s, vector<int>& c) {
            int f = 0;
            for (int i = 0; i < s.length(); ++i) {
                if (c[s[i] - '0'] > s[i] - '0') s[i] = c[s[i] - '0'] + '0', f = 1;
                else if (f && c[s[i] - '0'] < s[i] - '0') break;
            }
            return s;
        }
    };
    

    最大兼容性评分和

    给定 (n) 个题的试卷,每道题只有 (0, 1) 即对错两个答案

    给定 (m) 个学生和 (m) 个老师,每个学生和老师都有自己的 (n) 个答案

    现在每个学生分配给一个老师,把老师和学生相同答案的数量记为兼容值,要求计算一种方案,使得 (m) 对老师学生的兼容值的和最大,返回最大的兼容值的和

    数据规定

    (1leq n, mleq 8)

    题解

    数据非常小,考虑全排列枚举分配关系,使用 c++next_permutation

    考虑计算兼容值,可以把答案看作二进制数,利用异或计算海明距离,再使用 __builtin_popcount 可以求出不同值的个数,用 (n) 减去这个值即可

    时间复杂度 (O(m!cdot mn))

    当然也可以预处理老师学生的兼容值,时间复杂度 (O(m!cdot m + m^2n))

    class Solution {
    public:
        int maxCompatibilitySum(vector< vector< int > > &S, vector< vector< int > > &M)
        {
            int n = S[0].size();
            int m = S.size();
            int ans = 0;
            vector< int > per(m), a(m), b(m);
            for (int i = 0; i < m; ++i) {
                int sum1 = 0, sum2 = 0;
                for (int j = 0; j < n; ++j) {
                    sum1 *= 2, sum1 += S[i][j];
                    sum2 *= 2, sum2 += M[i][j];
                }
                a[i] = sum1, b[i] = sum2;
            }
            for (int i = 0; i < m; ++i) per[i] = i;
            do {
                int temp = 0;
                for (int i = 0; i < m; ++i) {
                    temp += n - __builtin_popcount(a[i] ^ b[per[i]]);
                }
                ans = max(ans, temp);
            } while (next_permutation(per.begin(), per.end()));
            return ans;
        }
    };
    

    删除系统中的重复文件夹

    给定一个二维字符串数组,用来表示文件系统,每个字符串数组构成绝对路径

    例如 [["a"],["c"],["d"],["a","b"],["c","b"],["d","a"]] 表示 a, c, d, a/b, c/b, d/a(6) 个文件

    对于 非空且拥有相同结构和相同文件夹的文件目录,我们要进行删除操作

    在上例中 a/b, c/b 是同样的结构,因此我们要删除这两个文件夹

    现在希望用一个二维字符串数组,仿照上面的路径表示法返回删除后的结果

    设共有 (n) 个绝对路径,每个绝对路径的长度为 (m_{i}),每个文件名长度为 (l_{ij})

    数据规定

    (1leq nleq 2cdot 10^4)

    (1leq m_{i}leq 500)

    (1leq lleq 10)

    (1leq sum l_{ij}leq2cdot 10^5)

    题解

    用一棵多叉树来维护文件系统,每个节点存储文件名和子树信息

    使用括号序列化将树结构映射成字符串,遍历树,得到所有子树的括号序列化值,并用哈希表维护序列值出现的次数

    再次遍历子树,只保存序列值出现一次的子树即可

    具体来讲,括号序列化是一种将树结构映射成字符串的方法

         r
     /   |   
     a   b   c
    

    上述树结构可以用 (r(a)(b)(c)) 来表示,获取括号序列化值通过一次 dfs 就能完成

    建树的过程可以考虑动态开点,绝对路径即为树上一条链,只要判断当前节点是否已经被父亲节点存储即可

    // cpp
    #define pb push_back
    struct node {
        string val, h;
        unordered_map< string, node * > son;  // 子树
        node() {}
        node(string _val): 
            val(_val) {}
    };
    class Solution {
    public:
        unordered_map< string, int > mp;
        void debug(vector<vector<string>> &ans)
        {
            for (auto &i : ans) {
                cout << "[";
                for (auto &j : i) {
                    cout << j << " ";
                }
                cout << "]" << endl;
            }
        }
        string dfs1(node *cur) // 第一次 dfs 获取子树的括号序列表达式
        {
            string temp;
            for (auto &i : cur->son) temp += dfs1(i.second);
            if (temp != "") mp[cur->h = temp]++; // 叶子结点不存在子节点,所以不存储
            return "(" + cur->val + temp + ")"; // 将括号序列表达式返回给父节点
        }
        void dfs2(node *cur, vector< vector< string > > &ans, vector< string > &pre) // 第二次 dfs 维护路径
        {
            if (mp[cur->h] > 1) return; // 子树的括号序列值出现了多次
            if (pre.size()) ans.pb(pre);
            for (auto &i : cur->son) {
                pre.pb(i.first); // 添加当前目录
                dfs2(i.second, ans, pre);
                pre.pop_back(); // 删除当前目录
            }
        }
        vector< vector< string > > deleteDuplicateFolder(vector< vector< string > > &p)
        {
            node *root = new node("/");
            for (auto &i : p) {
                node *cur = root;
                for (auto &j : i) {
                    if (!cur->son[j]) cur->son[j] = new node(j);
                    cur = cur->son[j];
                }
            }
            dfs1(root);
            vector< vector< string > > ans;
            vector< string > pre;
            dfs2(root, ans, pre);
            return ans;
        }
    };
    
    // go
    package main
    
    import "fmt"
    
    type node struct {
        val string
        h   string
        son map[string]*node
    }
    
    func newNode(_val string) *node {
        return &node{
            val: _val,
            son: make(map[string]*node),
        }
    }
    
    func deleteDuplicateFolder(p [][]string) (ans [][]string) {
    
        root := newNode("/")
        for _, i := range p {
            cur := root
            for _, j := range i {
                if _, ok := cur.son[j]; !ok {
                    cur.son[j] = newNode(j)
                }
                cur = cur.son[j]
            }
        }
    
        var mp = make(map[string]int)
        var dfs1 func(cur *node) string
        dfs1 = func(cur *node) string {
            var temp string
            for _, v := range cur.son {
                temp += dfs1(v)
            }
            if temp != "" {
                cur.h = temp
                mp[cur.h] += 1
            }
            return "(" + cur.val + temp + ")"
        }
        dfs1(root)
    
        pre := []string{}
        var dfs2 func(cur *node)
        dfs2 = func(cur *node) {
            if mp[cur.h] > 1 {
                return
            }
            if len(pre) > 0 {
                ans = append(ans, append([]string(nil), pre...))
            }
            for k, v := range cur.son {
                pre = append(pre, k)
                dfs2(v)
                pre = pre[:len(pre)-1]
            }
        }
        dfs2(root)
        return ans
    }
    
  • 相关阅读:
    SpringBoot异步方法优化处理提高响应速度
    sysbench性能测试
    lightdb停不掉FATAL: the database system is shutting down
    LOG: invalid primary checkpoint record/PANIC: could not locate a valid checkpoint record恢复方法
    postgresql/lightdb无法停止一例
    Program terminated with signal 6, Aborted
    PostgreSQL/lightdb逻辑复制详解
    LightDB Enterprise Postgres 22.1正式发布原生分布式版本
    postgresql/lightdb逻辑备份、恢复最佳实践
    Doris 与 ClickHouse 的深度对比及选型建议 徐正柱
  • 原文地址:https://www.cnblogs.com/ChenyangXu/p/15100851.html
Copyright © 2020-2023  润新知