【CSP】202403-2 相似度计算

2024年 第33次CCF计算机软件能力认证  202403-2 相似度计算

原题链接:相似度计算

时间限制: 1.0 秒

空间限制: 512 MiB

题目背景

两个集合的 Jaccard 相似度定义为:𝑆𝑖𝑚(𝐴,𝐵)=∣𝐴∩𝐵∣/∣𝐴∪𝐵∣即交集的大小除以并集的大小。当集合 𝐴 和 𝐵完全相同时,𝑆𝑖𝑚(𝐴,𝐵)=1取得最大值;当二者交集为空时,𝑆𝑖𝑚(𝐴,𝐵)=0取得最小值。

题目描述

除了进行简单的词频统计,小 P 还希望使用 Jaccard 相似度来评估两篇文章的相似性。 具体来说,每篇文章均由若干个英文单词组成,且英文单词仅包含“大小写英文字母”。 对于给定的两篇文章,小 P 首先需要提取出两者的单词集合 𝐴和 𝐵,即去掉各自重复的单词。 然后计算出:

  • ∣𝐴∩𝐵∣,即有多少个不同的单词同时出现在两篇文章中;
  • ∣𝐴∪𝐵∣,即两篇文章一共包含了多少个不同的单词。

最后再将两者相除即可算出相似度。 需要注意,在整个计算过程中应当忽略英文字母大小写的区别,比如 theThe 和 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[i].s和第二行单词b[i].s。把每个单词都视为一个字符串进行输入。

2.题目指出整个计算过程中应当忽略英文字母大小写的区别,比如 theThe 和 THE 三者都应被视作同一个单词。故而我们对输入的单词做一个统一化处理,统一转化为大写或小写,方便后面的运算,我这里是一边输入一边转成了小写。

3.去掉每行重复的单词,分别存到a[i].s1和b[i].s2中,同时开两个字符串把所有不重复的单词串成_aa_bb_cc_dd_这样的形式,方便后续利用find函数直接查找一个单词是否在字符串中。

查找子字符串str是否在字符串s中:利用string类find函数 格式为:s.find(str,开始位置),有则返回第一个匹配的下标,没有则返回-1.

4. ∣𝐴∪𝐵∣,即两篇文章一共包含了多少个不同的单词 = |A| + |B| -∣𝐴∩𝐵∣。

AC代码

#include<bits/stdc++.h>

using namespace std;
const int N = 10010; 
int n, m;

struct x{
    string s;
    string s1;
}a[N];

struct y{
    string s;
    string s2;
}b[N];	

bool cmp1(x a, x b)
{
    return a.s < b.s; 
}

bool cmp2(y a, y b)
{
    return a.s < b.s; 
}

string change(string s)
{
    int l = s.size();
    for(int i = 0; i < l ; i ++) if(s[i] < 'a') s[i] += 32;
    return s;
}

int main()
{
    cin >> n >> m;
        
    for(int i = 0; i < n; i ++)    //输入第一行 
    {
        cin >> a[i].s, a[i].s = change(a[i].s);
    }
    
    for(int i = 0; i < m; i ++)    //输入第二行 
    {
        cin >> b[i].s, b[i].s = change(b[i].s);
    }
    
    sort(a, a+n, cmp1);//把第1行的单词进行排序
    sort(b, b+m, cmp2);//把第2行的单词进行排序

    string sum1 = "_", sum2 = "_"; 
    //s1、s2里装的是每行里不重复的单词 
    //sum1、sum2是把这些单词累积串成一个字符串 方便后续查找单词
    a[0].s1 = a[0].s, sum1 += a[0].s1 + '_'; 
    b[0].s2 = b[0].s, sum2 += b[0].s2 + '_';

    int l1 = 1, l2 = 1; //设置第一行、第二行的单词初始长度
    
    if(a[0].s != a[n-1].s) //因为排过序了 所以首尾单词相同就相当于是只有一个“有效”单词
    { 
        for(int i = 1; i < n; i ++)
        {	//去掉重复单词 
            if(a[i].s != a[i-1].s) 
            {
                a[l1++].s1 = a[i].s, sum1 += a[i].s + '_';
            }
        }
    } 

    if(b[0].s != b[m-1].s)
    {
        for(int i = 1; i < m; i ++)
        {
            //去掉重复单词 
            if(b[i].s != b[i-1].s) 
            {
                b[l2++].s2 = b[i].s, sum2 += b[i].s + '_';
            }
        }
    }
    
    int ans1 = 0,ans2 = l1 + l2;
    
    if(l1 > l2) //(处理过后的)第1行比第2行长 
    {
        for(int i = 0; i < l2; i ++)
        {
            //若第2行的单词在第一行可以被找到,则交集+1
            //加一个‘_’在单词首尾的目的是作唯一性区分 防止把“the” 和“thee”匹配上这样子
            if(sum1.find('_'+b[i].s2+'_', 0) != -1) ans1 ++;
        }
    }
    else
    {
        for(int i = 0; i < l1; i ++)
        {
            if(sum2.find('_'+a[i].s1+'_', 0) != -1) ans1 ++;
        }
    }
    
    cout << ans1 << endl << ans2 - ans1 << endl;

    return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值