CCF CSP 第33次(2024.03)(2_相似度计算_C++)
- 题目背景:
- 题目描述:
- 输入格式:
- 输出格式:
- 样例1输入:
- 样例1输出:
- 样例1解释:
- 样例2输入:
- 样例2输出:
- 样例2解释:
- 样例3输入:
- 样例3输出:
- 子任务:
- 解题思路:
- 思路一(字符串中字母大小写转换+哈希集合):
- 代码实现
- 代码实现(思路一(字符串中字母大小写转换+哈希集合)):
时间限制: 1.0 秒
空间限制: 512 MiB
题目背景:
两个集合的 Jaccard 相似度定义为:
Sim(A,B)= ∣A∪B∣/∣A∩B∣
即交集的大小除以并集的大小。当集合 𝐴 和 𝐵完全相同时,𝑆𝑖𝑚(𝐴,𝐵)=1取得最大值;当二者交集为空时,𝑆𝑖𝑚(𝐴,𝐵)=0取得最小值。
题目描述:
除了进行简单的词频统计,小 P 还希望使用 Jaccard 相似度来评估两篇文章的相似性。 具体来说,每篇文章均由若干个英文单词组成,且英文单词仅包含“大小写英文字母”。 对于给定的两篇文章,小 P 首先需要提取出两者的单词集合 𝐴和 𝐵,即去掉各自重复的单词。 然后计算出:
∣𝐴∩𝐵∣,即有多少个不同的单词同时出现在两篇文章中;
∣𝐴∪𝐵∣,即两篇文章一共包含了多少个不同的单词。
最后再将两者相除即可算出相似度。 需要注意,在整个计算过程中应当忽略英文字母大小写的区别,比如 the、The 和 THE 三者都应被视作同一个单词。
试编写程序帮助小 P 完成前两步,计算出 ∣𝐴∩𝐵∣ 和 ∣𝐴∪𝐵∣;小 P 将亲自完成最后一步的除法运算。
输入格式:
从标准输入读入数据。
输入共三行。
输入的第一行包含两个正整数 𝑛 和 𝑚,分别表示两篇文章的单词个数。
第二行包含空格分隔的 𝑛 个单词,表示第一篇文章;
第三行包含空格分隔的 𝑚 个单词,表示第二篇文章。
输出格式:
输出到标准输出。
输出共两行。
第一行输出一个整数 ∣𝐴∩𝐵∣,即有多少个不同的单词同时出现在两篇文章中;
第二行输出一个整数 ∣𝐴∪𝐵∣,即两篇文章一共包含了多少个不同的单词。
样例1输入:
3 2
The tHe thE
the THE
样例1输出:
1
1
样例1解释:
𝐴=𝐵=𝐴∩𝐵=𝐴∪𝐵= {the}
样例2输入:
9 7
Par les soirs bleus dete jirai dans les sentiers
PICOTE PAR LES BLES FOULER LHERBE MENUE
样例2输出:
2
13
样例2解释:
𝐴=A= {bleus, dans, dete, jirai, les, par, sentiers, soirs} ∣𝐴∣=8
𝐵=B= {bles, fouler, les, lherbe, menue, par, picote} ∣𝐵∣=7
𝐴∩𝐵=A∩B= {les, par} ∣𝐴∩𝐵∣=2
样例3输入:
15 15
Thou that art now the worlds fresh ornament And only herald to the gaudy spring
Shall I compare thee to a summers day Thou art more lovely and more temperate
样例3输出:
4
24
子任务:
80% 的测试数据满足:𝑛,𝑚≤100且所有字母均为小写;
全部的测试数据满足:𝑛,𝑚≤104 且每个单词最多包含 10 个字母。
解题思路:
思路一(字符串中字母大小写转换+哈希集合):
1、解题步骤拆分:
① 忽略英文字母大小写,我们可以将所有的字母转换为小写。
② 忽略一个集合中重复的单词,我们可以想到哈希集合来进行降重。
③ 求并集,可以想到将两个集合中的并入一个集合。
④ 求交集,可以想到通过查找集合A中的元素是否存在集合B中来求出。
代码实现
代码实现(思路一(字符串中字母大小写转换+哈希集合)):
#include<iostream>
#include<unordered_set> // 引入unordered_set容器,使用哈希表来存储不重复元素
#include<algorithm> // 引入算法库,包含transform、tolower、toupper等函数using namespace std;int main(int argc, char const *argv[]) {int n, m;cin >> n >> m; // 输入两个整数 n 和 m,分别表示集合A和集合B的元素个数unordered_set<string> setA, setB; // 定义两个unordered_set,分别存储集合A和集合B的元素string word; // 用于存储每次输入的单词// 读取n个单词并插入集合setAfor (int i = 0; i < n; i++) {cin >> word;// 使用transform函数将单词转为小写 //tolower转小写 。toupper转为大写transform(word.begin(), word.end(), word.begin(), ::tolower); // tolower将字母转为小写setA.insert(word); // 将小写形式的单词插入setA}// 读取m个单词并插入集合setBfor (int i = 0; i < m; i++) {cin >> word;// 使用transform函数将单词转为小写transform(word.begin(), word.end(), word.begin(), ::tolower); // tolower将字母转为小写setB.insert(word); // 将小写形式的单词插入setB}unordered_set<string> intersection, unionSet; // 定义两个unordered_set,分别存储交集和并集// 计算集合B与集合A的交集 (也可以使用一个变量来计数)for (auto &str : setB) { // 遍历集合B中的每个元素if (setA.count(str)) { // 如果setA中包含该元素 //如果使用setA.find()则需与setA.end()进行比较判断有无intersection.insert(str); // 将该元素插入交集}}// 计算集合A与集合B的并集(也可以使用原来的set集合,将setB并入setA)unionSet = setA; // 将setA的所有元素先复制到unionSet中for (auto &str : setB) { // 遍历集合B中的每个元素unionSet.insert(str); // 将集合B中的元素插入unionSet(如果元素已经存在,insert不会重复插入)}// 输出交集的大小cout << intersection.size() << endl;// 输出并集的大小cout << unionSet.size() << endl;return 0; // 返回0,表示程序成功结束
}
//ch = std::toupper(ch); // 将字符转换为大写
欢迎大家和我沟通交流(✿◠‿◠)