相似度计算
刷新
时间限制: 1.0 秒
空间限制: 512 MiB
下载题目目录(样例文件)
题目背景
两个集合的 Jaccard 相似度定义为:𝑆𝑖𝑚(𝐴,𝐵)=∣𝐴∩𝐵∣∣𝐴∪𝐵∣Sim(A,B)=∣A∪B∣∣A∩B∣即交集的大小除以并集的大小。当集合 𝐴A 和 𝐵B 完全相同时,𝑆𝑖𝑚(𝐴,𝐵)=1Sim(A,B)=1 取得最大值;当二者交集为空时,𝑆𝑖𝑚(𝐴,𝐵)=0Sim(A,B)=0 取得最小值。
题目描述
除了进行简单的词频统计,小 P 还希望使用 Jaccard 相似度来评估两篇文章的相似性。 具体来说,每篇文章均由若干个英文单词组成,且英文单词仅包含“大小写英文字母”。 对于给定的两篇文章,小 P 首先需要提取出两者的单词集合 𝐴A 和 𝐵B,即去掉各自重复的单词。 然后计算出:
- ∣𝐴∩𝐵∣∣A∩B∣,即有多少个不同的单词同时出现在两篇文章中;
- ∣𝐴∪𝐵∣∣A∪B∣,即两篇文章一共包含了多少个不同的单词。
最后再将两者相除即可算出相似度。 需要注意,在整个计算过程中应当忽略英文字母大小写的区别,比如 the
、The
和 THE
三者都应被视作同一个单词。
试编写程序帮助小 P 完成前两步,计算出 ∣𝐴∩𝐵∣∣A∩B∣ 和 ∣𝐴∪𝐵∣∣A∪B∣;小 P 将亲自完成最后一步的除法运算。
输入格式
从标准输入读入数据。
输入共三行。
输入的第一行包含两个正整数 𝑛n 和 𝑚m,分别表示两篇文章的单词个数。
第二行包含空格分隔的 𝑛n 个单词,表示第一篇文章;
第三行包含空格分隔的 𝑚m 个单词,表示第二篇文章。
输出格式
输出到标准输出。
输出共两行。
第一行输出一个整数 ∣𝐴∩𝐵∣∣A∩B∣,即有多少个不同的单词同时出现在两篇文章中;
第二行输出一个整数 ∣𝐴∪𝐵∣∣A∪B∣,即两篇文章一共包含了多少个不同的单词。
样例1输入
3 2
The tHe thE
the THE
样例1输出
1
1
样例1解释
𝐴=𝐵=𝐴∩𝐵=𝐴∪𝐵=A=B=A∩B=A∪B= {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∣A∣=8
𝐵=B= {bles, fouler, les, lherbe, menue, par, picote}
∣𝐵∣=7∣B∣=7
𝐴∩𝐵=A∩B= {les, par}
∣𝐴∩𝐵∣=2∣A∩B∣=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%80% 的测试数据满足:𝑛,𝑚≤100n,m≤100 且所有字母均为小写;
全部的测试数据满足:𝑛,𝑚≤104n,m≤104 且每个单词最多包含 1010 个字母。
先吐槽一下沟槽的CCF。不能提交看自己的代码能不能ac了。
这个题我一开始想到的是检索。
代码对不对我不清楚,给的样例全过了,我这个思路的话时间复杂度很小。
总体的思路是a,b,c等26个字母赋值为1,2,3,4-.....26这样的值,用一个结构体储存。如果要判断这个字符是否已经出现,假入我们现在用的是字符串判定,那么每一个字符都要判定,增加了时间复杂度,我们可以在转化成小写的时候做一些处理。我们可以记录路径过程:abc路径过程为321,这样可以保证每一个字符串有唯一的对应的路径。也需要记录路径的和:还是上面的情况,路径和为1+2+3=6。路径和可以作为检索的下标(最大也就260,10个z)。为什么不采用路径过程作为检索下标呢?因为路径过程的值很大,需要非常大的空间(可以达到longlong范围)。我们采用vector二维数组来储存两个字符串。储存a,b(第一个和第二个)字符串的时候顺便记录重复的单词temp_same。在记录b时,我们还需要记录多少个不同的单词同时出现在两篇文章中cnt_same。最后结果就是cnt_same和n+m-temp_same-cnt_same。
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
struct A
{
long long l=0;//记录路径 假设a=1,b=2,c=3;那么abc为321;
int tot=0;//记录路径总和假设a=1,b=2,c=3;那么abc为6;
int cnt=0;//记录出现了多少次
bool j=0;
string s;
}a;
vector<vector<A> >v(300);
vector<vector<A> >v1(300);
int cnt_same=0;
bool w_0(int temp )
{
return ((temp/10)!=0);
}
A lowercase(string s)
{
int l=s.length();
A a;
a.s=s;
int chang=0,temp;//记录a.l长度
for(int i=0;i<l;i++)
{
if(s[i]<'a')//大写情况
{
temp=s[i]-'A'+1;
a.tot+=temp;
a.l+=temp*pow(10,chang);
chang+=w_0(temp)+1;
}
else
{
temp=s[i]-'a'+1;
a.tot+=temp;
a.l+=temp*pow(10,chang);
chang+=w_0(temp)+1;
}
}
return a;
}
int main()
{
int n,m;
string s;
cin>>n>>m;
int temp_same=0;//记录每一行字符串自己内部重复的总数量
for(int i=0;i<n;i++)
{
cin>>s;
a=lowercase(s);
int flag=0;
for(int j=0;j<v[a.tot].size();j++)
{
if(v[a.tot][j].l==a.l)
{
v[a.tot][j].cnt++;
temp_same++;
flag=1;
break;
}
}
if(flag==0)
{
a.cnt++;
v[a.tot].push_back(a);
}
}//第一个字符串处理完毕
for(int i=0;i<m;i++)
{
cin>>s;
a=lowercase(s);
int flag=0;
for(int j=0;j<v[a.tot].size();j++)
{
if(v[a.tot][j].l==a.l)
{
if(v[a.tot][j].j==0)
{
v[a.tot][j].j=1;
cnt_same++;
}
}
}//记录多少相同的单词
for(int j=0;j<v1[a.tot].size();j++)
{
if(v1[a.tot][j].l==a.l)
{
v1[a.tot][j].cnt++;
temp_same++;
flag=1;
break;
}
}
if(flag==0)
{
a.cnt++;
v1[a.tot].push_back(a);
}
}
cout<<cnt_same<<endl;
cout<<m+n-temp_same-cnt_same<<endl;
}