华为OD机考算法题:垃圾信息拦截

2023-09-21 23:45:00

目录

题目部分

解读与分析

代码实现


题目部分

题目垃圾信息拦截
难度
题目说明大众对垃圾短信深恶痛绝,希望能对垃圾短信发送者进行识别,为此,很多软件增加 了垃圾短信识别机制。经分析,发现正常用户的短信通常具备交互性,而垃圾短信往 往都是大量单向的短信,按照如下规则进行垃圾短信识别:
本题中,发送者A符合以下条件之一的,则认为A是垃圾短信发送者:

· A 发送短信的接收者中,没有发过短信给A的人数 L > 5;
· A 发送的短信数 - A接收的短信数 M > 10;
· 如果存在 X,A 发送给 X 的短信数 - A 接收到X的短信数N > 5。
输入描述第一行为条目数目,接下来几行是具体的条目,每个条目,是一对ID,第一个数字是发送者ID,后面的数字是接收者ID,中间空格隔开,所有的ID都为无符号整型,ID最大值为100;
同一个条目中,两个ID不会相同(即不会自己给自己发消息);
最后一行为指定的ID。
输出描述输出该ID是否为垃圾短信发送者(是输出 true,否则输出 false),并且按序列输出L、M的值(由于N值不唯一,不需要输出);
输出均为宇符串。
补充说明
------------------------------------------------------
示例
示例1
输入15
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
1 11
1 12
1 13
1 14
14 1
1 15
1
输出true 13 13
说明true 表示 1 是垃圾短信发送者。两个 13 代表发送者 1 对应的 L 和 M 值都是 13。true 13 13 之间用一个空格隔开。
注意:true 是字符串输出。
示例2
输入15
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
1 11
1 12
1 13
1 14
14 1
1 15
2
输出false 0 -1
说明


解读与分析

题目解读

通过题目要求的三条规则对用户进行判断。如果三条规则满足一条,则此用户为垃圾短信发送者。

分析与思路

逐行读取短信发送信息,每行作为一个 string,存储到数组 inputs 中。
读取最后一行,读取要检查的 id,设为 idA。

申明如下变量:
1. receivers,一个集合,记录 idA 这个短信发送者发送短信的所有接收者 id。
2. senders, 一个集合,记录所有给 idA 发送短信的用户 id。
3. aSendCnt,整型数字,初始值为 0。记录 idA 发送的短信条数。
4. aReceiveCnt,整型数字,初始值为 0。记录  idA 接收的短信条数。
5. msgSendCntMap,一个 map,记录 idA 发送给其他用户,和其他用户发送给 idA 的短信条数。其中 key 的格式为 idA + " " +“其他用户id” (代表 idA 给其他用户发短信), 或 “其他用户id” + " " + idA(代表其他用户给 idA 发短信),值为整型数字,表示短信条数。

实现方法如下:
1. 遍历 inputs,对于 inputs中的每条字符串,设为 inputEle,以空格(" ")分隔,把它分成一个数组 inputEleSplits,其中 inputEleSplits[0] 为 空格前的 id,即短信发送者的 id;inputEleSplits[1] 为短信接收者的 id。对 inputEle 和 inputEleSplits 进行如下判断:
① 如果 inputEleSplits[0] 和 inputsEleSplits[1] 都不等于 idA,那么忽略这条信息(即不做任何处理)。
② 如果 inputEleSplits[0] 等于 idA,那么把 inputEleSplits[1] 加到 receivers 中去,并把 aSendCnt 加 1。与此同时,获取 msgSendCntMap.get( inputEle ) 的值,如果值为空,则赋值为 1;若不为空,则把其值加 1。
③ 如果 inputEleSplits[1] 等于 idA,那么把 inputEleSplits[0] 加到 senders 中去,并把 aReceiveCnt 加 1。与此同时,获取 msgSendCntMap.get( inputEle ) 的值,如果值为空,则赋值为 1;若不为空,则把其值加 1。

2. 遍历完 inputs 后,receivers、senders、aSendCnt、aReceiveCnt、msgSendCntMap均已初始化完毕。下一步,进行入操作:
① 申请变量 recSendCnt,初始值为 0。用以记录既给 idA 发过信息,也收到过 idA 的信息的用户数。遍历 senders 的每个元素,如果元素在 receivers 中,则 recSendCnt 加 1。遍历完后,集合receviers 的元素个数与 recSendCnt 的差,即为 L 的值。
② 用 aSendCnt 减去 aReceiveCnt,即为 M 的值。
③ 先假设第三条规则不存在这样的用户,申明变量布尔变量 exsitsX ,并赋值为 false。遍历 receivers,设每个元素为 tmpReceiver,分别求 msgSendCntMap.get( idA + " " + tmpReceiver ) 和 msgSendCntMap.get( tmpReceiver + " " +  idA ) 的值,如果为空,则赋值为 0。求前者减去后者的差值,如果差值大于 5,则 existsX 赋值为 true,退出遍历;否则继续遍历下一个 tmpReceiver。
④ 如果 L > 5 或 M > 5 或 existsX == true,则此用户为垃圾短信发送者。
最后,输出对应的值。

此题时间复杂度 O(n),空间复杂度 O(n)。


代码实现

Java代码

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/**
 * 垃圾信息拦截
 * @since 2023.09.20
 * @version 0.1
 * @author Frank
 *
 */
public class MsgSpam {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while (sc.hasNext()) {
			String input = sc.nextLine();
			int count = Integer.parseInt( input );
			
			String[] inputs = new String[count];
			for( int i = 0; i < count; i ++ )
			{
				input = sc.nextLine();
				inputs[i] = input;
			}
			
			String idA = sc.nextLine();
			processMsgSpam( idA, inputs );
		}
	}

	private static void processMsgSpam( String idA, String inputs[] )
	{
		Set<String> receivers = new HashSet<String>();
		Set<String> senders = new HashSet<String>();
		int aSendCnt = 0;
		int aReceiveCnt = 0;
		Map<String, Integer> msgSendCntMap = new HashMap<String, Integer>();
		
		for( int i = 0; i < inputs.length; i ++ )
		{
			String inputEle = inputs[i];
			String[] inputEleSplits = inputEle.split( " " );
			
			if( ( !inputEleSplits[0].equals( idA ) ) &&  ( !inputEleSplits[1].equals( idA ) )  )
			{
				continue;
			}
			
			if( inputEleSplits[0].equals( idA ) )
			{
				receivers.add( inputEleSplits[1] );
				aSendCnt += 1;
			}else // inputEleSplits[1].equals( idA ) 
			{
				senders.add( inputEleSplits[0] );
				aReceiveCnt += 1;
			}
			Integer tmpCnt = msgSendCntMap.get( inputEle );
			if( tmpCnt == null )
			{
				tmpCnt = 0;
			}
			tmpCnt += 1;
			msgSendCntMap.put( inputEle, tmpCnt );				
		}
		
		int recSendCnt = 0;
		for( Iterator<String> iter = senders.iterator(); iter.hasNext(); )
		{
			String tmpSender = iter.next();
			if( receivers.contains( tmpSender ) )
			{
				recSendCnt ++;
			}
		}
		int L = receivers.size() - recSendCnt;
		
		int M = aSendCnt - aReceiveCnt;
		
		boolean existsX = false;
		for( Iterator<String> iter = receivers.iterator(); iter.hasNext(); )
		{
			String tmpReceiver = iter.next();
			Integer tmpSendCnt = msgSendCntMap.get( idA + " " + tmpReceiver );
			if( tmpSendCnt == null )
			{
				// will never come here
				continue;
			}
		
			Integer tmpReceiveCnt = msgSendCntMap.get( tmpReceiver + " " + idA );
			if( tmpReceiveCnt == null )
			{
				tmpReceiveCnt = 0;
			}
			if( tmpSendCnt - tmpReceiveCnt > 5 )
			{
				existsX = true;
				break;
			}
		}
		
		boolean isSpamSender = ( L > 5 ) || ( M > 5) || existsX;
		System.out.println( isSpamSender + " " + L + " " + M );
	}
}

JavaScript代码

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function() {
    while (line = await readline()) {
        var count = parseInt(line);

        var inputs = new Array();
        for (var i = 0; i < count; i++) {
            inputs[i] = await readline();
        }

        var idA = await readline();
        processMsgSpam(idA, inputs);
    }
}();

function processMsgSpam(idA, inputs) {
    var receivers = new Set();
    var senders = new Set();
    var aSendCnt = 0;
    var aReceiveCnt = 0;
    var msgSendCntMap = new Map();

    for (var i = 0; i < inputs.length; i++) {
        var inputEle = inputs[i];
        var inputEleSplits = inputEle.split(" ");

        if ((inputEleSplits[0] != idA) && (inputEleSplits[1] != idA)) {
            continue;
        }

        if (inputEleSplits[0] == idA) {
            receivers.add(inputEleSplits[1]);
            aSendCnt += 1;
        } else // inputEleSplits[1].equals( idA ) 
        {
            senders.add(inputEleSplits[0]);
            aReceiveCnt += 1;
        }
        var tmpCnt = msgSendCntMap.get(inputEle);
        if (tmpCnt == null) {
            tmpCnt = 0;
        }
        tmpCnt += 1;
        msgSendCntMap.set(inputEle, tmpCnt);
    }

    var recSendCnt = 0;
    for (const item of senders) {
        if (receivers.has(item)) {
            recSendCnt++;
        }
    }
    var L = receivers.size - recSendCnt;

    var M = aSendCnt - aReceiveCnt;

    var existsX = false;
    for (const item of receivers) {
        var tmpSendCnt = msgSendCntMap.get(idA + " " + item);
        if (tmpSendCnt == null) {
            // will never come here
            continue;
        }

        var tmpReceiveCnt = msgSendCntMap.get(item + " " + idA);
        if (tmpReceiveCnt == null) {
            tmpReceiveCnt = 0;
        }
        if (tmpSendCnt - tmpReceiveCnt > 5) {
            existsX = true;
            break;
        }
    }

    var isSpamSender = (L > 5) || (M > 5) || existsX;
    console.log(isSpamSender + " " + L + " " + M);
}

(完)

更多推荐

selenium+python实现基本自动化测试

安装selenium打开命令控制符输入:pipinstall-Uselenium火狐浏览器安装firebug:www.firebug.com,调试所有网站语言,调试功能SeleniumIDE是嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作的录制与回放功能,IDE录制的脚本可以可以转换成多种语言,从而帮助我

蓝桥杯 题库 简单 每日十题 day5

01字符计数字符计数题目描述给定一个单词,请计算这个单词中有多少个元音字母,多少个辅音字母。元音字母包括a,e,i,o,u,共五个,其他均为辅音字母。输入描述输入格式:输入一行,包含一个单词,单词中只包含小写英文字母。单词中的字母个数不超过100。输出描述输出两行,第一行包含一个整数,表示元音字母的数量。第二行包含一个

【运维 Pro】时序场景实践与原理 - 2. 宽表,窄表与 JSON 字段

【运维Pro】:由YMatrix售前和售后团队负责的栏目。除了介绍日常的数据库运维和使用知识,我们更希望能够通过介绍这些知识背后的原理,让大家和我们一起感知数据库的美妙。摘要在上一期《时序场景实践与原理-1.分布与分区》中,我们围绕时间戳和设备标识列,介绍了设计关于分区、分布的设计思路和原理;在本期内容中,我们会围绕指

SpringBoot项目Redis使用

SpringBoot项目Redis使用引入依赖<!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>对R

【C语言】求一个整数的二进制序列中1的个数的三种方法

方法一:逐位%2法该方法的初步测试代码如下:intNumberOf1(intn){intcount=0;while(n){if(n%2==1){count++;}n=n/2;}returncount;}众所周知,数据在内存里以补码的形式存储,这是为了简化计算机的结构设计,同时也提高了运算速度。因此在计算机系统中,数值一

C语言字符串数组的定义方式

方法1:定义一个char类型的二维数组charstr[4][20]={"IloveC","Iloveyou","C语言","string"};这种方法是通过定义一个char类型的二维数组实现,通过二维数组的行索引可得到数组中的每个字符串,列的大小限定了每个字符串所能包含的最大字符个数,所以采用这种定义方式时,列的大小必

Camunda自定义多实例审批人列表

Camunda自定义多实例审批人列表1.多实例案例在工作流中会有遇到这样一个"多个人处理同一个任务“的情形,在camunda中可以使用"任务的多实例"来实现。这里以或签为例,可以设置完成条件为${nrOfCompletedInstances==1},如果是会签,设置成${nrOfCompletedInstances==

windows下gvim的配置

一、vim配置文件"查看自己的vimrc所在的目录"在命令模式下:echo$MYVIMRC"打开自己的vimrc文件"在命令模式下:e$MYVIMRC二、排版"查看自己当前的字体及大小"在命令模式下:setguifont?"设置默认的字体为仿宋_GB2312,大小为14号"在vimrc文件中添加setguifont=仿

嵌入式:驱动开发 Day4

作业:通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定驱动程序:myled.c#include<linux/init.h>#include<linux/module.h>#include<linux/cdev.h>#include<linux/fs.h>#include<linux/device.h

echart在折线显示横纵(横纵线沿着折线展示)

产品有个需求,需要在echart折线上展示横纵向坐标系,echart的axisPointer默认是展示在鼠标当前位置的,不符合需求,所以是使用markline实现的在线例子和源码先上效果图实现思路横纵线的x轴线是比较容易的,因为echart的axixPointer的位置是鼠标当前坐标作的,所以x轴线直接用toltip的

【K8S系列】深入解析k8s网络插件—Cilium

序言做一件事并不难,难的是在于坚持。坚持一下也不难,难的是坚持到底。文章标记颜色说明:黄色:重要标题红色:用来标记结论绿色:用来标记论点蓝色:用来标记论点在现代容器化应用程序的世界中,容器编排平台Kubernetes已经成为标准。为了支持复杂的应用和微服务架构,网络是Kubernetes集群中不可或缺的一部分。本文将深

热文推荐