1.题目
问题描述
小C参与了一场抢红包的游戏,现在他想要对所有参与抢红包的人进行一次运气排名。排名规则如下:抢到的金额越多,排名越靠前;如果两个人抢到的金额相同,则按照他们抢红包的顺序进行排名。比如,如果小C和小U抢到的金额相同,但小C比小U先抢,则小C排在小U前面。
测试样例
样例1:
输入:n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1]
输出:
['b', 'c', 'a', 'd']
样例2:
输入:n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200]
输出:
['y', 'z', 'x']
样例3:
输入:n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20]
输出:
['m', 'n', 'o', 'p', 'q']
2.思路
一个人可以多次抢红包,需要对这个人抢红包的金额累加后再比较,如果金额相同,则按第一次抢到红包的时间先后排序。
定义两个unordered_map start和cnt ,start 存储了字符串作为键(key),索引作为值(value)。键是字符串,值是该字符串在输入列表 s 中第一次出现的索引(位置),cnt 存储了字符串作为键(key),对应的整数值(value)是该字符串所有出现位置上 x[i] 数值的总和。
定义vector a,存放cnt中出现的所有名字,按照cnt值降序排序,若cnt值相同,则按照start索引值升序排序,对a排序,最后return a,即为排序后的结果。
3.代码
最初的理解
最初不知道一个人可以多次抢红包,所以按一个人只出现一次去写逻辑,不能通过所有测试样例。
#include <iostream>
#include <utility>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
vector<string> solution(int n, vector<string> s, vector<int> x) {
// write code here
map<string, int> nameToAmount;
for (int i = 0; i < n; i++){
nameToAmount[s[i]] = x[i];
}
// 将map中的元素转移到vector中
vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());
// 对vector进行排序,先按红包金额降序排序,红包金额相同的按名字再原map中的顺序排序
sort(nameAmountPairs.begin(), nameAmountPairs.end(), [](const pair<string, int>& a, const pair<string, int>& b){
if (a.second != b.second) {
return a.second > b.second;
}
return false;
});
//提取排序后的名字
vector<string> result;
for (const auto& pair : nameAmountPairs) {
result.push_back(pair.first);
}
return result;
}
int main() {
cout << (solution(4, {"a", "b", "c", "d"}, {1, 2, 2, 1}) == vector<string>{"b", "c", "a", "d"}) << endl;
cout << (solution(3, {"x", "y", "z"}, {100, 200, 200}) == vector<string>{"y", "z", "x"}) << endl;
cout << (solution(5, {"m", "n", "o", "p", "q"}, {50, 50, 30, 30, 20}) == vector<string>{"m", "n", "o", "p", "q"}) << endl;
return 0;
}
代码详解:
vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());
vector<pair<string, int>>: 定义了一个vector,其中每个元素是一个pair<string, int>,表示名字和对应的红包金额。nameToAmount.begin(): 返回nameToAmount这个map的起始迭代器。nameToAmount.end(): 返回nameToAmount这个map的结束迭代器。vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());: 使用map的迭代器范围构造一个vector,将map中的所有元素复制到vector中。
第四个样例有误
输入
n= 12
S = ["aa","aaaaaaa","aaaa","aaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaaa","aaaa"]
X = [17,14,11,2,8,16,14,17,10,6,5,12]
输出
你的输出 = ["aa","aaaaaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaaa"]
预期输出 = ["aaaa","aaaaaaaaaa","aaaaaaaaa","aa","aaaaaaa","aaaaa"]
这道题题目不是特别清楚,实际上是可以一个人多次抢红包,需要对这个人抢红包的金额累加后再比较。 我的代码中遇到一个人多次抢红包时,将这个人的信息更新到了最后一次出现的情况。具体来说,假设有一个人在多个时刻(多次)参与了抢红包的操作,每次出现时,会更新这个人对应的数据,但最终保留的是他在最后一次出现时的状态,而之前的状态被覆盖。
修改逻辑
输入答案后生成的Python代码如下,可以提交通过
def solution(n: int, s: list, x: list) -> list:
assert n == len(s) == len(x)
start = {}
cnt = {}
for i in range(n):
if s[i] not in start.keys():
start[s[i]] = i
cnt[s[i]] = cnt.get(s[i], 0) + x[i]
a = sorted(cnt.keys(), key=lambda s: (-cnt[s], start[s]))
return a
if __name__ == '__main__':
print(solution(n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1]) == ['b', 'c', 'a', 'd'])
print(solution(n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200]) == ['y', 'z', 'x'])
print(solution(n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20]) == ['m', 'n', 'o', 'p', 'q'])
但用AI转成cpp它会换逻辑,还是不能通过(😵)
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
vector<string> solution(int n, vector<string> s, vector<int> x) {
// 将名字和红包金额存放到map中
map<string, int> nameToAmount;
for (int i = 0; i < n; i++) {
nameToAmount[s[i]] = x[i];
}
// 将map中的元素转移到vector中
vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());
// 对vector进行排序,先按红包金额降序排序,红包金额相同的按名字在原map中的顺序排序
sort(nameAmountPairs.begin(), nameAmountPairs.end(), [](const pair<string, int>& a, const pair<string, int>& b) {
if (a.second != b.second) {
return a.second > b.second; // 按红包金额降序排序
}
return a.first < b.first; // 红包金额相同的按名字在原map中的顺序排序
});
// 提取排序后的名字
vector<string> result;
for (const auto& pair : nameAmountPairs) {
result.push_back(pair.first);
}
return result;
}
int main() {
cout << (solution(4, {"a", "b", "c", "d"}, {1, 2, 2, 1}) == vector<string>{"b", "c", "a", "d"}) << endl;
cout << (solution(3, {"x", "y", "z"}, {100, 200, 200}) == vector<string>{"y", "z", "x"}) << endl;
cout << (solution(5, {"m", "n", "o", "p", "q"}, {50, 50, 30, 30, 20}) == vector<string>{"m", "n", "o", "p", "q"}) << endl;
return 0;
}
用ChatGPT重新转了c++,可以通过:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
vector<string> solution(int n, vector<string>& s, vector<int>& x) {
unordered_map<string, int> start;
unordered_map<string, int> cnt;
for (int i = 0; i < n; ++i) {
if (start.find(s[i]) == start.end()) {
start[s[i]] = i; // 记录每个字符串第一次出现的索引
}
cnt[s[i]] += x[i]; // 累加每个字符串对应的 x 值
}
// 按照 cnt 值降序排序,若 cnt 值相同,则按照 start 索引升序排序
vector<string> a;
for (const auto& pair : cnt) {
a.push_back(pair.first);
}
sort(a.begin(), a.end(), [&cnt, &start](const string& s1, const string& s2) {
if (cnt[s1] != cnt[s2]) {
return cnt[s1] > cnt[s2]; // 按照 cnt 值降序排序
}
return start[s1] < start[s2]; // 如果 cnt 值相同,则按 start 索引升序排序
});
return a;
}
int main() {
// 测试案例
vector<string> s1 = {"a", "b", "c", "d"};
vector<int> x1 = {1, 2, 2, 1};
vector<string> result1 = solution(4, s1, x1);
for (const auto& str : result1) cout << str << " "; // 输出: b c a d
cout << endl;
vector<string> s2 = {"x", "y", "z"};
vector<int> x2 = {100, 200, 200};
vector<string> result2 = solution(3, s2, x2);
for (const auto& str : result2) cout << str << " "; // 输出: y z x
cout << endl;
vector<string> s3 = {"m", "n", "o", "p", "q"};
vector<int> x3 = {50, 50, 30, 30, 20};
vector<string> result3 = solution(5, s3, x3);
for (const auto& str : result3) cout << str << " "; // 输出: m n o p q
cout << endl;
return 0;
}
代码详解:
unordered_map 中的元素是 无序的,即它们并不按照插入的顺序或者键的大小顺序排列,而是通过哈希值来组织存储的。
if (start.find(s[i]) == start.end())
- 如果找到键
s[i],find返回指向该元素的迭代器。 - 如果找不到,
find返回指向容器尾部的迭代器,即start.end()。 start.find(s[i]) == start.end()的条件是s[i]不在start中,也就是第一次遇到s[i]这个字符串。
sort(a.begin(), a.end(), [&cnt, &start](const string& s1, const string& s2){
if (cnt[s1] != cnt[s2]) {
return cnt[s1] > cnt[s2];
}
return start[s1] < start[s2];
});
sort(a.begin(), a.end(), ...)
排序使用了第三个参数:一个自定义的排序函数(这里是一个 lambda 表达式),这个函数定义了排序的规则。
- Lambda 表达式
[&cnt, &start](const string& s1, const string& s2)
这是一个lambda 表达式,即一个匿名的函数对象。它接受两个参数 s1 和 s2,它们是待比较的字符串。s1 和 s2 对应于排序中正在比较的两个元素。
[&cnt, &start]:这里使用了 捕获列表,意思是:
&cnt和&start表示捕获cnt和start两个外部变量的引用,供 lambda 表达式内使用。
if (cnt[s1] != cnt[s2])
这个条件判断用来比较 cnt 中 s1 和 s2 对应的值:
cnt[s1]表示s1对应的总和(数值)。cnt[s2]表示s2对应的总和(数值)。
如果 cnt[s1] 和 cnt[s2] 不相同,则根据它们的值进行排序:
return cnt[s1] > cnt[s2];:如果cnt[s1]大于cnt[s2],则返回true,表示s1应该排在s2前面(降序排序)。
return start[s1] < start[s2];
如果 cnt[s1] == cnt[s2](即 s1 和 s2 对应的值相同),则比较它们在 start 中的索引。
start[s1]表示字符串s1第一次出现的索引。start[s2]表示字符串s2第一次出现的索引。
start[s1] < start[s2] 表示如果 s1 的第一次出现位置较早(即索引较小),则 s1 应该排在 s2 前面(升序排序)。
整体排序逻辑:
首先,根据 cnt[s1] 和 cnt[s2] 的大小进行排序,降序排列(cnt[s1] > cnt[s2])。
如果 cnt 值相同,则使用 start[s1] 和 start[s2] 的索引进行排序,升序排列(start[s1] < start[s2])。