2025B-垃圾短信识别-200分

刷题笔记合集🔗

问题描述

随着移动通信的普及,垃圾短信也越来越多,给人们的生活带来了很多烦恼。为了识别垃圾短信发送者,我们需要分析短信发送和接收的模式。

在这个问题中,我们将使用以下规则来识别垃圾短信发送者:

  1. 如果一个用户A发送短信给超过5个没有回复过A的用户,则A可能是垃圾短信发送者。
  2. 如果一个用户A发送的短信总数比收到的短信总数多10条以上,则A可能是垃圾短信发送者。
  3. 如果存在用户X,用户A发送给X的短信比X发送给A的短信多5条以上,则A可能是垃圾短信发送者。

如果满足以上任一条件,则认为该用户是垃圾短信发送者。

输入格式

第一行包含一个整数 ,表示短信记录的数量。

接下来的 行,每行包含两个整数 ,分别表示发送者ID和接收者ID。

最后一行包含一个整数 ,表示要检查的用户ID。

输出格式

输出一行,包含三个值,用空格分隔:

  • 第一个值是 "true" 或 "false",表示用户 是否是垃圾短信发送者。
  • 第二个值是一个整数 ,表示用户 发送短信给的没有回复过 的用户数量。
  • 第三个值是一个整数 ,表示用户 发送的短信总数减去收到的短信总数。

样例输入1

12
1 2
1 3
1 4
1 5
1 6
1 7
1 8
2 1
3 10
4 10
5 10
6 10
1

样例输出1

true 6 7

样例输入2

15
2 1
2 1
2 1
2 1
2 1
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1

样例输出2

false 0 5

样例输入3

10
1 2
1 2
1 2
1 2
1 2
1 2
1 2
2 1
3 4
5 6
1

样例输出3

true 0 6
样例 解释说明
样例1 用户1发送短信给用户2、3、4、5、6、7、8,但只有用户2回复了用户1,所以l=6。用户1发送了7条短信,收到了1条短信,所以m=6。由于l>5,所以用户1是垃圾短信发送者。
样例2 用户1发送短信给用户2,用户2也回复了用户1,所以l=0。用户1发送了10条短信,收到了5条短信,所以m=5。由于l≤5且m≤10,且不存在用户X使得用户1发送给X的短信比X发送给用户1的短信多5条以上,所以用户1不是垃圾短信发送者。
样例3 用户1发送7条短信给用户2,但只收到用户2的1条短信,所以用户1发送给用户2的短信比用户2发送给用户1的短信多6条,满足条件3,因此用户1是垃圾短信发送者。

数据范围

题解

这道题目要求我们识别垃圾短信发送者,需要根据三个条件来判断:

  1. 发送短信给超过5个没有回复过的用户
  2. 发送的短信总数比收到的短信总数多10条以上
  3. 存在用户X,发送给X的短信比X发送给自己的短信多5条以上

解题思路:

  1. 统计用户发送和接收的短信记录
  2. 计算没有回复过的用户数量l
  3. 计算发送短信总数与接收短信总数的差值m
  4. 检查是否存在满足条件3的用户X
  5. 根据三个条件判断是否是垃圾短信发送者

时间复杂度分析:

  • 统计短信记录需要 的时间
  • 计算l和m需要 的时间,其中k是与用户tid有交互的用户数量
  • 检查条件3需要 的时间
  • 因此,总的时间复杂度是

空间复杂度分析:

  • 存储短信记录需要 的空间
  • 因此,总的空间复杂度是

对于给定的数据范围,这个算法是高效的,可以在合理的时间内得到结果。

参考代码

# 输入获取
n = int(input())
msg_list = [input().split() for i in range(n)]
user_id = input()

# 算法入口
def check_spam(uid, msgs):
    # 记录发送和接收的消息
    out_msgs = []
    in_msgs = []
    
    # 记录发送和接收的次数
    out_cnt = {}
    in_cnt = {}
    
    # 处理所有消息记录
    for s_id, r_id in msgs:
        # 处理发送的消息
        if s_id == uid:
            out_msgs.append(r_id)
            if r_id in out_cnt:
                out_cnt[r_id] += 1
            else:
                out_cnt[r_id] = 1
        
        # 处理接收的消息
        if r_id == uid:
            in_msgs.append(s_id)
            if s_id in in_cnt:
                in_cnt[s_id] += 1
            else:
                in_cnt[s_id] = 1
    
    # 计算发送和接收的用户集合
    out_set = set(out_msgs)
    in_set = set(in_msgs)
    
    # 计算有交互的用户
    chat_list = [x for x in out_set if x in in_set]
    
    # 计算关键指标
    no_reply = len(out_set) - len(chat_list)
    msg_diff = len(out_msgs) - len(in_msgs)
    
    # 判断是否为垃圾短信发送者
    is_spam = no_reply > 5 or msg_diff > 10
    
    # 如果前两个条件不满足,检查第三个条件
    if not is_spam:
        for uid2 in out_set:
            sent = out_cnt[uid2]
            recv = in_cnt.get(uid2, 0)
            if sent - recv > 5:
                is_spam = True
                break
    
    # 返回结果
    return f"{str(is_spam).lower()} {no_reply} {msg_diff}"

# 算法调用
print(check_spam(user_id, msg_list))
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <string>
using namespace std;

/**
 

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

算法刷题笔记 文章被收录于专栏

本专栏收集并整理了一些刷题笔记

全部评论

相关推荐

05-23 12:00
门头沟学院 C++
5.22一面,总共时长125min1.如何保护用户的隐私2.int*&nbsp;createArray()&nbsp;{int&nbsp;arr[3]&nbsp;=&nbsp;{1,&nbsp;2,&nbsp;3};&nbsp;return&nbsp;arr;}&nbsp;这段代码有什么问题3.对上述代码进行改进,写出能想到的所有方法(我写了一个malloc,全局数组,静态数组)4.说一下三种方式的优缺点5.全局数组和静态数组有什么区别6.解释完美转发的作用及实现方式7.const&nbsp;int*,int&nbsp;const*,int*&nbsp;const,&nbsp;const&nbsp;int*&nbsp;const的区别8.实现一个无锁计数器9.调用C++11实现一个线程安全的有界环形队列(circular&nbsp;buffer),要求如下:支持多线程环境下的并发push和pop操作,队列有固定容量,满时push操作要阻塞,空时pop操作要阻塞。不允许使用第三方库,只能用C++11标准库,说明你的实现如何保证线程安全,并分析可能的性能瓶颈。10.unique_lock&nbsp;和&nbsp;lock_guard的区别,为什么你刚才给我的代码用的是unique_lock&nbsp;而不是&nbsp;lock_guard&nbsp;呢11.你写的代码的性能瓶颈是什么?如果有大量得到生产者和消费者会怎样呢12.把第9个改成非阻塞的,写一下,为什么你这个非阻塞用lock_guard这个锁呢13.有一类二叉树用三叉链表来存储的时候除了带有指向左右孩子节点的两个指针,还有指向父节点的指针,那么这样一棵二叉树有n个节点,那么有多少指针指向NULL(对于不存在的节点表示为空)14.int&nbsp;n&nbsp;=&nbsp;2019;&nbsp;int&nbsp;count&nbsp;=&nbsp;0;&nbsp;&nbsp;while(n){count++;&nbsp;n&nbsp;=&nbsp;n&amp;(n&nbsp;-&nbsp;1);}&nbsp;cout&nbsp;&lt;&lt;&nbsp;count&nbsp;&lt;&lt;&nbsp;endl;输出是多少,为什么15.给定一个递增循环整数数组,从里面找出最小的元素,使用的算法越快越好。特别地,最小的元素可能出现在数组中间。比如:50,52,63,90,3,8,15,44,49,int&nbsp;findmin(int&nbsp;array[]){}16.在二叉排序树上面找出第3大的节点。注意:不能把二叉树全量存储到另外的存储空间,比如存储到数组中,然后取出数组的第三个元素。class&nbsp;TreeNode&nbsp;{public:int&nbsp;value;TreeNode*left;TreeNode*&nbsp;right};TreeNode*&nbsp;find(TreeNode*root)&nbsp;{}17.动态规划题:给定一个长度为l的木棍,已知有n个切割点,要求在每个切割点都要切割,注意每次切割的开销为当前木棍的长度,例如一个10米的木棍,切割点为2,4,7。有多种切割方式,其中可以先切2,再切4,再切7,此时开销为10+8+6=24(第1次切木棍为10米,笑2次切木棍为8米,第3次切木棍为6米),也可以先切4,再切2,再切7,出约著销为10+4+6=20,这时开销更小你的任务是计算切割的最小开销。
查看17道真题和解析
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务