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]
)。