单周赛 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\) 对老师学生的兼容值的和最大,返回最大的兼容值的和
数据规定
\(1\leq n,\ m\leq 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}\)
数据规定
\(1\leq n\leq 2\cdot 10^4\)
\(1\leq m_{i}\leq 500\)
\(1\leq l\leq 10\)
\(1\leq \sum l_{ij}\leq2\cdot 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
}