字符串编码与解码
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
题目描述
给你两个长度相同的字符串a,b,现在已知b是a编码之后的结果
比如a = "CAT", b = "DOG", 那么D其实是C,O其实是A,G其实是T
现在给你一个字符串c,如果c能够被解码出来,输出c解码后的字符串,如果不能输出@
输入描述:
输入三行,每行一个字符串,长度在50以内
输出描述:
输出一个字符串
示例1
输入
复制CAT DOG GOD
CAT
DOG
GOD
输出
复制TAC
TAC
示例2
输入
复制BANANA METETE TEMP
BANANA
METETE
TEMP
输出
复制@
@
示例3
输入
复制THEQUICKBROWNFOXJUMPSOVERTHELAZYHOG UIFRVJDLCSPXOGPYKVNQTPWFSUIFMBAZIPH DIDYOUNOTICESKIPPEDLETTER
THEQUICKBROWNFOXJUMPSOVERTHELAZYHOG
UIFRVJDLCSPXOGPYKVNQTPWFSUIFMBAZIPH
DIDYOUNOTICESKIPPEDLETTER
输出
复制CHCXNTMNSHBDRJHOODCKDSSDQ
CHCXNTMNSHBDRJHOODCKDSSDQ
说明
E之外的其他字母一一对应之后,E只能对应D
备注:
子任务1:|a| <= 10
子任务2:|a| <= 40
子任务3:无限制
1.
题目意思给出三个字符串分别是a,b,c.
a字符串通过一些编码规则转化为b,也就是b通过一些解码规则解码成a.
现在给我们c字符串,希望将c字符串种所有的字母全部解码,如果能够全部解码就输出解码之后的字符串,如果不能全部解码就输出@.
2.
注意看示例三,示例三告诉我们解码规则并没有包含所有的字母,他只包含了25个字母,但是这25个字母一一对应,因此最后一个字母只能匹配一个字母,所以这种情况需要补充一个解码规则.
3.
解题思路,遍历a,b字符串,收集所有的解码规则放到hash表中,这样我们可以快速查询到某一个字母解码之后的字母是什么,然后遍历c字符串,如果有一个字母找不到解码规则,就输出@,否则全部解码然后输出.
4.
如何快速找到25个字母解码规则一一对应,需要补充一个解码规则的情况,只需要用count1和count2记录解码规则使用过的字母和映射过去的字母使用情况即可.
如果两者都等于25说明此时需要补充解码规则.
用index1和index2记录没有使用过的字母,这样就可以快速找到需要补充的解码规则.
#include<bits/stdc++.h> // 引入标准库中的所有头文件
using namespace std;
#define int long long // 定义int为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
string a, b, c; // 声明三个字符串a, b, c
map<char, char> jiema; // 定义一个映射表,用于存储解码规则
vector<bool> visited1; // 用于记录b中出现的字母
int count1 = 0; // 记录b中不同字母的数量
int index1 = -1; // 记录b中未出现字母的索引
vector<bool> visited2; // 用于记录a中出现的字母
int count2 = 0; // 记录a中不同字母的数量
int index2 = -1; // 记录a中未出现字母的索引
string ret; // 用于存储解码后的结果字符串
void init() {
visited1.assign(26, false); // 初始化visited1向量,大小为26,所有值为false
visited2.assign(26, false); // 初始化visited2向量,大小为26,所有值为false
for (int i = 0; i < a.size(); i++) { // 遍历字符串a和b的每个字符
jiema[b[i]] = a[i]; // 将b的字符映射到a的字符,存入jiema映射表
visited1[b[i] - 'A'] = true; // 标记b中的字符
visited2[a[i] - 'A'] = true; // 标记a中的字符
}
for (int i = 0; i < 26; i++) { // 遍历26个字母
if (visited1[i] == true) {
count1++; // 统计b中出现的不同字母数量
} else {
index1 = i; // 记录b中未出现的字母的索引
}
if (visited2[i] == true) {
count2++; // 统计a中出现的不同字母数量
} else {
index2 = i; // 记录a中未出现的字母的索引
}
}
if (count1 == count2 && count1 == 25) { // 如果b和a中都有25个不同字母
jiema[index1 + 'A'] = index2 + 'A'; // 将最后一个未使用的字母进行映射
}
}
void solve() {
for (int i = 0; i < c.size(); i++) { // 遍历字符串c的每个字符
if (jiema.count(c[i])) { // 如果c中的字符在jiema映射表中
ret.push_back(jiema[c[i]]); // 将对应的解码字符加入结果字符串
} else {
cout << "@" << endl; // 如果有字符不能解码,输出@并返回
return;
}
}
cout << ret << endl; // 输出解码后的结果字符串
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> a >> b >> c; // 输入三个字符串a, b, c
init(); // 初始化映射表
solve(); // 解码字符串c并输出结果
return 0; // 返回0,表示程序执行成功
}
【模板】01背包
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
题目描述
你有一个背包,最多能容纳的体积是V。
现在有n个物品,第i个物品的体积为viv_ivi ,价值为wiw_iwi。
(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?
输入描述:
第一行两个整数n和V,表示物品个数和背包体积。
接下来n行,每行两个数viv_ivi和wiw_iwi,表示第i个物品的体积和价值。
1≤n,V,vi,wi≤10001 \le n,V,v_i,w_i \le 10001≤n,V,vi,wi≤1000
输出描述:
输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。
示例1
输入
复制3 5 2 10 4 5 1 4
3 5
2 10
4 5
1 4
输出
复制14 9
14
9
说明
装第一个和第三个物品时总价值最大,但是装第二个和第三个物品可以使得背包恰好装满且总价值最大。
示例2
输入
复制3 8 12 6 11 8 6 8
3 8
12 6
11 8
6 8
输出
复制8 0
8
0
说明
装第三个物品时总价值最大但是不满,装满背包无解。
备注:
要求O(nV)的时间复杂度,O(V)空间复杂度
二进制暴力枚举(超时)
1.
0/1背包组合问题.一共有2^n种方案,对于每一个物品可以选择或者不选择,一共有n个物品,对于第一个物品可以选择或者不选择,两种方案,对于第二个物品可以选择或者不选择,两种方案,........2*2*2....
一共的方案数是2^n种.
2.
在每一个方案中,对于每一个物品都可以选择或者不选择,可以用1表示选择,用0表示不不选择,所以需要n个位置填上1或者0表示每一个物品的使用情况.如果这n个位置是二进制的形式,那么000..00~111..11一共是2^n个,最大值是2^n-1.
所以只需要遍历0~2^n-1,对于每一个数的二进制数就是一种方案,然后对于每一种方案找到对应的选择的物品即可.
#include<bits/stdc++.h> // 引入所有标准库头文件
using namespace std;
#define int long long // 将int定义为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
int n, v; // 物品数量和背包容量
struct node {
int weight = 0; // 物品的重量
int value = 0; // 物品的价值
};
vector<node> wupin; // 存储所有物品的向量
int ret1 = 0; // 第一问的答案,背包最多能装的最大价值
int ret2 = 0; // 第二问的答案,背包恰好装满时的最大价值
int ncount = 0; // 方案总数
void init() {
ncount = pow(2, n); // 计算2^n,总方案数
}
void solve() {
for (int i = 0; i < ncount; i++) { // 遍历所有方案
node temp; // 临时节点存储当前方案的总重量和总价值
for (int j = 0; j < n; j++) { // 遍历每个物品
if (temp.weight > v) break; // 如果当前方案的重量超过背包容量,退出
if (i & (1 << j)) { // 判断方案i的第j位是否为1,即是否选择该物品
temp.value += wupin[j].value; // 加上该物品的价值
temp.weight += wupin[j].weight; // 加上该物品的重量
}
}
if (temp.weight <= v) // 如果当前方案的重量不超过背包容量
if (ret1 < temp.value) ret1 = temp.value; // 更新第一问的答案
if (temp.weight == v) // 如果当前方案的重量恰好等于背包容量
if (ret2 < temp.value) ret2 = temp.value; // 更新第二问的答案
}
cout << ret1 << endl << ret2 << endl; // 输出答案
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> n >> v; // 输入物品数量和背包容量
wupin.assign(n, {0, 0}); // 初始化物品向量
for (int i = 0; i < n; i++) {
cin >> wupin[i].weight >> wupin[i].value; // 输入每个物品的重量和价值
}
init(); // 初始化方案总数
solve(); // 解决问题并输出答案
}
自顶向下的动态规划,记忆化递归(超时)
#include<bits/stdc++.h> // 引入所有标准库头文件
using namespace std;
#define int long long // 将int定义为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
struct node {
int weight; // 物品的重量
int value; // 物品的价值
};
int n, v; // 物品数量和背包容量
vector<node> wupin; // 存储所有物品的向量
vector<vector<int>> dp; // 动态规划数组
// 定义一个递归函数,解决01背包问题的第一问
int dfs(int i, int weight) {
if (weight < 0) return LLONG_MIN; // 如果重量小于0,返回负无穷表示不可行
if (weight == 0) return 0; // 如果重量正好为0,返回0价值
if (i < 0) return 0; // 如果没有物品可选,返回0价值
if (dp[i][weight] != -1) return dp[i][weight]; // 如果dp值已经计算过,直接返回
dp[i][weight] = max(dfs(i - 1, weight), wupin[i].value + dfs(i - 1, weight - wupin[i].weight)); // 选择不放和放当前物品的最大值
return dp[i][weight];
}
// 定义另一个递归函数,解决01背包问题的第二问
int dfs1(int i, int weight) {
if (weight < 0) return LLONG_MIN; // 如果重量小于0,返回负无穷表示不可行
if (weight == 0) return 0; // 如果重量正好为0,返回0价值
if (i < 0) return LLONG_MIN; // 如果没有物品可选,返回负无穷表示不可行
if (dp[i][weight] >= 0) return dp[i][weight]; // 如果dp值已经计算过,直接返回
dp[i][weight] = max(dfs1(i - 1, weight), wupin[i].value + dfs1(i - 1, weight - wupin[i].weight)); // 选择不放和放当前物品的最大值
return dp[i][weight];
}
// 初始化dp数组
void init() {
dp = vector<vector<int>>(n, vector<int>(v + 1, -1)); // 初始化为(n, v+1)的二维数组,值为-1
}
void solve() {
for (int i = 0; i < n; i++) // 遍历每个物品
for (int j = 0; j <= v; j++) { // 遍历每个可能的重量
dfs(i, j); // 计算dfs
dfs1(i, j); // 计算dfs1
}
cout << dfs(n - 1, v) << endl; // 输出第一问的答案
init(); // 重新初始化dp数组
cout << (dfs1(n - 1, v) >= 0 ? dfs(n - 1, v) : 0) << endl; // 输出第二问的答案,如果没有可行解输出0
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> n >> v; // 输入物品数量和背包容量
wupin.assign(n, {0, 0}); // 初始化物品向量
for (int i = 0; i < n; i++) {
cin >> wupin[i].weight >> wupin[i].value; // 输入每个物品的重量和价值
}
init(); // 初始化dp数组
solve(); // 解决问题并输出答案
}
自底向上的动态规划
1.
状态转移方程根据递归的思维找到.
需要注意的就是填表顺序,填表顺序根据状态转移方程判断.
dp[i][j] = max(dp[i - 1][j], wupin[i].value + dp[i - 1][j - wupin[i].weight]);
这是状态转移方程,当我们填写(i,j)位置的dp值,需要用到(i-1,j)和(i-1,j - wupin[i].weight])位置的dp值.
所以i一定从小到大,j也一定从小到大.
2.
边界情况用if条件判断一下,防止越界的情况.
对于dp[i][j]表示从i下标之前的物品进行选择,最多花费j体积获得的最大价值.这种情况,第一个考虑的越界情况是i==0的时候,此时只有一个物品,只需要判断此时j体积和物品体积比较一下即可.
接下来判断的是j - wupin[i].weight]越界的情况.
3.
dp[i][j]表示从i下标之前的物品进行选择,刚好花费j体积获得的最大价值.此时的边界处理多了一些操作.
用-1表示的是无法获取价值,如果无法获取价值就不考虑这种情况.
#include <bits/stdc++.h> // 引入所有标准库头文件
using namespace std;
#define int long long // 将int定义为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
struct node {
int value = 0; // 物品的价值
int weight = 0; // 物品的重量
};
int n = 0, v = 0; // 物品数量和背包容量
vector<node> wupin; // 存储所有物品的向量
vector<vector<int>> dp; // 动态规划数组
void init() {
dp.assign(n, vector<int>(v + 1, 0)); // 初始化dp数组,大小为n*(v+1),初始值为0
}
void solve() {
// dp[i][j]表示从i下标之前的物品进行选择,最多花费j体积获得的最大价值
for (int i = 0; i < n; i++) {
for (int j = 0; j <= v; j++) {
if (i == 0) { // 边界情况:只有一个物品
dp[i][j] = (j >= wupin[i].weight) ? wupin[i].value : 0; // 判断是否能放下物品,能放下则取物品价值,否则为0
} else if (j < wupin[i].weight) { // 当前背包容量不足以放下当前物品
dp[i][j] = dp[i - 1][j]; // 继承前一个状态的值
} else {
dp[i][j] = max(dp[i - 1][j], wupin[i].value + dp[i - 1][j - wupin[i].weight]); // 取不放当前物品和放当前物品两种情况的最大值
}
}
}
cout << dp[n - 1][v] << endl; // 输出第一问的答案:背包最多能装的最大价值
init(); // 重新初始化dp数组
// dp[i][j]表示从i下标之前的物品进行选择,刚好花费j体积获得的最大价值
for (int i = 0; i < n; i++) {
for (int j = 0; j <= v; j++) {
if (i == 0) { // 边界情况:只有一个物品
dp[i][j] = (j == wupin[i].weight) ? wupin[i].value : -1; // 判断是否恰好能放下物品,能放下则取物品价值,否则为-1
if (j == 0) dp[i][j] = 0; // 如果重量为0,价值也为0
} else if (j < wupin[i].weight) { // 当前背包容量不足以放下当前物品
dp[i][j] = dp[i - 1][j]; // 继承前一个状态的值
} else {
if (dp[i - 1][j - wupin[i].weight] == -1) { // 如果剩余容量无法放下物品
dp[i][j] = dp[i - 1][j]; // 继承前一个状态的值
} else {
dp[i][j] = max(dp[i - 1][j], wupin[i].value + dp[i - 1][j - wupin[i].weight]); // 取不放当前物品和放当前物品两种情况的最大值
}
}
}
}
cout << (dp[n - 1][v] >= 0 ? dp[n - 1][v] : 0) << endl; // 输出第二问的答案:背包恰好装满时的最大价值,如果无解输出0
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> n >> v; // 输入物品数量和背包容量
wupin.assign(n, {0, 0}); // 初始化物品向量
for (int i = 0; i < n; i++)
cin >> wupin[i].weight >> wupin[i].value; // 输入每个物品的重量和价值
init(); // 初始化dp数组
solve(); // 解决问题并输出答案
}
wnm的全排列
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
题目描述
wnm最近迷上了全排列,简单的输出一个字符串所有的全排列已经难不住他了。最近他遇到了一个新问题,一个仅由0-9组成的字符串,对于它所有排列所构成的数字按升序排列后的序列,第n个数是多少呢?
输入描述:
首先输入一个正整数t(1<=t<=1000),表示共有t组测试数据。
对于每组测试数据,每行包含一个仅由0-9组成的字符串s(1<=|s|<=6,保证每个字符只会出现一次)和一个正整数n(1<=n<=1000)。
输出描述:
请你找到这个字符串所有的排列所构成的数字按升序排列后的第n个数字,如果存在就输出这个数字,否则输出"no solution"。
示例1
输入
复制3 120 1 321 2 213 10
3
120 1
321 2
213 10
输出
复制Case 1: 12 Case 2: 132 Case 3: no solution
Case 1: 12
Case 2: 132
Case 3: no solution
说明
对于样例1:120所有排列所构成的数字按升序排序后的序列为12,21,102,120,201,210,所以第1个数字是12。
对于样例2:321所有排列所构成的数字按升序排序后的序列为123,132,213,231,312,321,所以第2个数字是132。
对于样例3:213所有排列所构成的数字按升序排序后的序列同样例2,不存在第10个数字。
1.
全排列问题,用内置函数next_permutation找到所有的全排列情况.
next_permutation(arr.begin(),arr.end()).使得arr变成下一个全排列的形式.
也就是将所有的全排列按照某一个逻辑排序了,next_permutation只是找到下一个全排列的序列形式.
人为认为第一个全排列的序列是默认sort排序后的序列.
所以使用之前需要将序列sort排序一下.
2.
解题思路就是找到所有的全排列序列,然后放到一个ret数组中,然后将ret数组排序直接返回第n个输出即可.
#include <bits/stdc++.h> // 引入所有标准库头文件
using namespace std;
#define int long long // 将int定义为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
int t; // 测试数据组数
string s; // 输入的仅由0-9组成的字符串
int n; // 要求的第n个全排列数字
vector<string> sortstr; // 存储所有全排列的向量
void init() {
sortstr.clear(); // 清空sortstr向量
}
void solve() {
sort(s.begin(), s.end()); // 将字符串按升序排序
do {
sortstr.push_back(s); // 将当前排列加入sortstr向量
} while (next_permutation(s.begin(), s.end())); // 生成下一个排列,直到没有更多排列
sort(sortstr.begin(), sortstr.end()); // 对所有排列再进行一次排序,确保按升序排列
if (n <= sortstr.size()) { // 如果n不超过排列的数量
cout << stoll(sortstr[n - 1]) << endl; // 输出第n个排列
} else {
cout << "no solution" << endl; // 否则输出“no solution”
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> t; // 输入测试数据组数
int count = 1; // 记录测试数据的编号
while (t--) {
cin >> s >> n; // 输入字符串和要求的第n个排列
cout << "Case " << count++ << ": "; // 输出测试数据的编号
init(); // 初始化,清空sortstr
solve(); // 解决当前测试数据
}
return 0; // 返回0,表示程序执行成功
}
非递归实现组合型枚举
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
题目描述
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n>0n \gt 0n>0, 0≤m≤n0 \leq m \leq n0≤m≤n, n+(n−m)≤25n+(n-m)\leq 25n+(n−m)≤25。
输入描述:
两个整数n,m。
输出描述:
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。
示例1
输入
复制5 3
5 3
输出
复制1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
1.
组合型枚举,固定n和m,因此可以用全排列的方式去找所有的组合形式.
思路是用一个vector<bool>开辟n个空间,分别对应n个物品,其中m个位置为true.表示这m个物品选择.
然后找到所有的全排列形式,此时就找到了所有的组合序列.
#include<bits/stdc++.h> // 引入所有标准库头文件
using namespace std;
#define int long long // 将int定义为long long类型,方便处理大数
#define endl '\n' // 定义endl为换行符
int n, m; // 物品数量n和选择数量m
vector<bool> findnums; // 存储是否选择某个物品的布尔向量
vector<vector<int>> ret; // 存储所有组合的结果
void init() {
findnums.assign(n, false); // 初始化findnums向量,大小为n,所有值为false
for(int i = 0; i < m; i++) { // 将前m个位置设置为true,表示选择前m个物品
findnums[i] = true;
}
sort(findnums.begin(), findnums.end()); // 将findnums向量按升序排序
}
void solve() {
do {
vector<int> temp; // 临时向量,存储当前组合
for(int i = 0; i < n; i++) {
if(findnums[i]) { // 如果findnums[i]为true,表示选择该物品
temp.push_back(i + 1); // 将物品编号(从1开始)加入临时向量
}
}
ret.push_back(temp); // 将当前组合加入结果向量
} while(next_permutation(findnums.begin(), findnums.end())); // 生成下一个排列,直到没有更多排列
sort(ret.begin(), ret.end()); // 对所有组合结果进行排序
for(const auto& vec : ret) { // 遍历每个组合
for(const auto& nums : vec) { // 遍历组合中的每个数字
cout << nums << " "; // 输出数字
}
cout << endl; // 输出换行符
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
cin >> n >> m; // 输入物品数量n和选择数量m
init(); // 初始化findnums向量
solve(); // 生成并输出所有组合结果
}
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!