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

目录

题目部分

解读与分析

代码实现


题目部分

题目垃圾信息拦截
难度
题目说明大众对垃圾短信深恶痛绝,希望能对垃圾短信发送者进行识别,为此,很多软件增加 了垃圾短信识别机制。经分析,发现正常用户的短信通常具备交互性,而垃圾短信往 往都是大量单向的短信,按照如下规则进行垃圾短信识别:
本题中,发送者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);
}

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值