2024华为OD笔试机试 - 根据IP查找城市 (Java/c++/python D卷C卷真题算法)

华为OD机试(C卷+D卷)2024真题目录(Java & c++ & python)

题目描述

某业务需要根据终端的IP地址获取该终端归属的城市,可以根据公开的IP地址池信息查询归属城市。

地址池格式如下:

城市名=起始IP,结束IP

起始和结束地址按照英文逗号分隔,多个地址段采用英文分号分隔。比如:

City1=1.1.1.1,1.1.1.2;City1=1.1.1.11,1.1.1.16;City2=3.3.3.3,4.4.4.4;City3=2.2.2.2,6.6.6.6

一个城市可以有多个IP段,比如City1有2个IP段。

城市间也可能存在包含关系,如City3的IP段包含City2的IP段范围。

现在要根据输入的IP列表,返回最佳匹配的城市列表。

注:最佳匹配即包含待查询IP且长度最小的IP段,比如例子中3.4.4.4最佳匹配是City2=3.3.3.3,4.4.4.4,5.5.5.5的最佳匹配是City3=2.2.2.2,6.6.6.6

输入描述

输入共2行。

第一行为城市的IP段列表,多个IP段采用英文分号 ‘;’ 分隔,IP段列表最大不超过500000。城市名称只包含英文字母、数字和下划线。最多不超过100000个。IP段包含关系可能有多层,但不超过100层。

第二行为查询的IP列表,多个IP采用英文逗号 ‘,’ 分隔,最多不超过10000条。

输出描述

最佳匹配的城市名列表,采用英文逗号 ‘,’ 分隔,城市列表长度应该跟查询的IP列表长度一致。

备注

  • 无论是否查到匹配正常都要输出分隔符。举例:假如输入IP列表为IPa,IPb,两个IP均未有匹配城市,此时输出为",",即只有一个逗号分隔符,两个城市均为空;
  • 可以假定用例中的所有输入均合法,IP地址均为合法的ipv4地址,满足 (1255).(0255).(0255).(0255) 的格式,且可以假定用例中不会出现组播和广播地址;

用例

输入

City1=1.1.1.1,1.1.1.2;City1=1.1.1.11,1.1.1.16;City2=3.3.3.3,4.4.4.4;City3=2.2.2.2,6.6.6.6
1.1.1.15,3.3.3.5,2.2.2.3

输出

City1,City2,City3

说明
1)City1有2个IP段,City3的IP段包含City2的IP段;
2)1.1.1.15仅匹配City1=1.1.1.11,1.1.1.16,所以City1就是最佳匹配;2.2.2.3仅匹配City3=2.2.2.2,6.6.6.6,所以City3是最佳匹配;3.3.3.5同时匹配为City2=3.3.3.3,4.4.4.4和City3=2.2.2.2,6.6.6.6,但是City2=3.3.3.3,4.4.4.4的IP段范围更小,所以City3为最佳匹配;

解题思路

  • IP地址实际上每一位是2^8,整个地址则可以用 2^32的整型来表示,直接通过数值判断则更高效。
  • 遍历待查询的IP地址,去和每一个IP段范围匹配,如果可以匹配上,且对应IP段范围更小或相等时取更晚匹配的,则对应IP段的城市就是当前待查询IP的最佳匹配城市。
  • 实战小技巧:由于碰巧数据是升序给的,有的人会以为是相等范围时按字典序大的优先,估计就是出题者出数据用的题解程序写多了等号,以至于出现这种情况。所以如果在其他题遇到类似情况过不了全部数据时,都可以尝试加多个等号,没准就全部通过了!

C++、Java、Python代码如下:

C++参考代码

#include <bits/stdc++.h>
using namespace std;

vector<string> split(const string &s, char delimiter) {
    stringstream ss(s);
    string token;
    vector<string> result;
    
    while (getline(ss, token, delimiter)) {
        result.emplace_back(token);
    }
    
    return result;
}

// IP地址转为整型
long ipToDec(const string &ip) {
    long result = 0;
    stringstream ss(ip);
    string segment;
    
    while (getline(ss, segment, '.')) {
        result = (result << 8) | stoi(segment);
    }
    
    return result;
}

class Range {
public:
    string city;
    long startIp;
    long endIp;
    long rangeLength;
    
    Range(string cityName, const string &startIpStr, const string &endIpStr) {
        city = std::move(cityName);
        startIp = ipToDec(startIpStr);
        endIp = ipToDec(endIpStr);
        rangeLength = endIp - startIp + 1;
    }
};

int main() {
    vector<Range> ipRanges;
    
    // 城市IP列表
    string citiesInput;
    cin >> citiesInput;
    vector<string> cities = split(citiesInput, ';');
    
    // 待查询的IP列表
    string queriesInput;
    cin >> queriesInput;
    vector<string> queryIps = split(queriesInput, ',');
    
    // 解析每个城市的IP范围
    for (const auto &cityData : cities) {
        vector<string> cityAndRange = split(cityData, '=');
        string &cityName = cityAndRange[0];
        vector<string> ipRange = split(cityAndRange[1], ',');
        
        ipRanges.emplace_back(cityName, ipRange[0], ipRange[1]);
    }
    
    bool firstOutput = true;
    
    // 遍历待查询的IP地址
    for (const auto &queryIp : queryIps) {
        long ipDec = ipToDec(queryIp);
        string matchedCity;
        long smallestRange = LONG_MAX;
        
        // 检查IP地址属于哪个城市范围
        for (const auto &range : ipRanges) {
            if (ipDec >= range.startIp && ipDec <= range.endIp && range.rangeLength <= smallestRange) {
                matchedCity = range.city;
                smallestRange = range.rangeLength;
            }
        }
        
        if (firstOutput) {
            firstOutput = false;
        } else {
            cout << ",";
        }
        
        cout << matchedCity;
    }
    
    cout << endl;
    return 0;
}

Java参考代码

import java.util.ArrayList;
import java.util.Scanner;
import java.util.StringJoiner;

public class Main {
  static class Range {
    String city;
    long startIpDec;
    long endIpDec;
    long ipRangeSize;

    public Range(String city, String startIpStr, String endIpStr) {
      this.city = city;
      // 将IP地址转为整型
      this.startIpDec = ipToDec(startIpStr);
      this.endIpDec = ipToDec(endIpStr);
      this.ipRangeSize = this.endIpDec - this.startIpDec + 1;
    }
  }

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    ArrayList<Range> ranges = new ArrayList<>();

    // 城市IP列表
    String[] cities = sc.nextLine().split(";");
    // 带查询的IP列表
    String[] queryIps = sc.nextLine().split(",");

    // 提取各个城市IP列表信息
    for (String city : cities) {
      String[] tmp = city.split("[=,]");
      ranges.add(new Range(tmp[0], tmp[1], tmp[2]));
    }

    StringJoiner result = new StringJoiner(",");

    // 遍历待查询的IP地址
    for (String ip : queryIps) {
      long ipDec = ipToDec(ip);

      // 记录该目标IP地址的最佳匹配城市
      String bestMatchCity = "";
      // 记录最佳匹配城市IP段的长度
      long smallestRangeSize = Long.MAX_VALUE;

      // 将待查询IP与城市IP段列表逐一匹配
      for (Range range : ranges) {
        // 如果待查询的IP地址在某城市的IP段范围内,且该城市的IP段长度更小,则该城市为待查询IP的最佳匹配城市
        if (ipDec >= range.startIpDec && ipDec <= range.endIpDec && smallestRangeSize >= range.ipRangeSize) {
          bestMatchCity = range.city;
          smallestRangeSize = range.ipRangeSize;
        }
      }

      result.add(bestMatchCity);
    }

    System.out.println(result);
  }

  // IP地址转整型
  public static long ipToDec(String ip) {
    long result = 0;

    String[] blocks = ip.split("\\.");

    for (String block : blocks) {
      result = (Integer.parseInt(block)) | (result << 8);
    }

    return result;
  }
}

Python参考代码

import re
import sys

# IP地址转整型
def ip_to_dec(ip_str):
    res = 0
    blocks = ip_str.split(".")
    for block in blocks:
        res = int(block) | (res << 8)
    return res

# 表示IP范围的类
class IPRange:
    def __init__(self, city, start_ip_str, end_ip_str):
        self.city = city
        self.start_ip = ip_to_dec(start_ip_str)  # 将IP地址转为整型
        self.end_ip = ip_to_dec(end_ip_str)
        self.ip_len = self.end_ip - self.start_ip + 1

# 输入获取
city_data = input().split(";")  # 城市IP列表
query_ips = input().split(",")  # 带查询的IP列表

# 处理城市IP范围信息
ip_ranges = []
for data in city_data:
    # 使用解包的方式将split的结果传入构造函数
    ip_ranges.append(IPRange(*(re.split(r"[=,]", data))))

# 核心代码
results = []

# 遍历待查询的IP地址
for ip in query_ips:
    ip_dec = ip_to_dec(ip)
    best_city = ""
    min_len = sys.maxsize

    # 查找最佳匹配城市
    for ip_range in ip_ranges:
        if ip_range.start_ip <= ip_dec <= ip_range.end_ip and ip_range.ip_len <= min_len:
            best_city = ip_range.city
            min_len = ip_range.ip_len

    results.append(best_city)

# 输出结果
print(",".join(results))

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值