算法刷题 - 字符串(验证IP地址)

178 阅读3分钟

原题链接

LeetCode 468 - 验证 IP 地址

题目描述

给定一个字符串 queryIP。如果是有效的 IPv4 地址,返回 "IPv4";如果是有效的 IPv6 地址,返回 "IPv6" ;如果不是上述类型的 IP 地址,返回 "Neither"

有效的 IPv4 地址 是 “x1.x2.x3.x4” 形式的IP地址。 其中 0 <= xi <= 255 且 xi 不能包含 前导零。例如: “192.168.1.1” 、 “192.168.1.0” 为有效IPv4地址, “192.168.01.1” 为无效IPv4地址; “192.168.1.00”“192.168@1.1” 为无效IPv4地址。

一个有效的IPv6地址 是一个格式为 “x1:x2:x3:x4:x5:x6:x7:x8” 的IP地址,其中:

  • 1 <= xi.length <= 4 xi 是一个 十六进制字符串 ,可以包含数字、小写英文字母( 'a' 到 'f' ) 和 大写英文字母 ( 'A' 到 'F' )
  • 在 xi 中允许前导零。
  • 例如 "2001:0db8:85a3:0000:0000:8a2e:0370:7334""2001:db8:85a3:0:0:8A2E:0370:7334" 是有效的 IPv6 地址,
  • "2001:0db8:85a3::8A2E:037j:7334""02001:0db8:85a3:0000:0000:8a2e:0370:7334" 是无效的 IPv6 地址。

输入示例

这里写图片替代文字

思路

整体:

  • 如果字符串长度大于 32 + 7 = 39; 则不可能是 IPv4 也不可能是 IPv6;
  • (优化) IPv4 的最小长度需要为 7 (0.0.0.0); 字符串长度小于 7 也符合任意格式

验证是否为 IPv4:

  • 按照 . 分割需要长度为 4
  • 对于每一段; 如果该段长度大于 1 则不能包含前导 0
  • 每一段需要满足正则表达式 \\d+; 不能包含非数字的字符
  • 借助Integer.parseInt转化成整数后需要满足范围 [0 ~ 255]

验证是否为 IPv6:

  • 将所有字符转化为小写字符,统一方便处理
  • 按照 : 分割需要满足长度为 8
  • 分割后每一段需要满足正则表达式 ^[0-9a-f]{1,4}$
    • 数字可包含 0 ~ 9
    • 字符可以包含小写 a-f (由于统一 toLowCase 处理,不考虑大写)
    • 并且每一段需要满足长度 1 ~ 4

(优化) 借助正则表达式

对于频繁使用的情况,可以考虑将正则表达式编译为 Pattern 对象,然后复用该对象进行匹配,这样可以提高正则表达式匹配的效率。

// 提前编译验证 IPv4 的正则表达式
private Pattern IPV4_SEGMENT_PATTERN = Pattern.compile("\\d+");

// 提前编译验证 IPv6 的正则表达式
private Pattern IPV6_SEGMENT_PATTERN = Pattern.compile("^[0-9a-f]{1,4}$");

代码

import java.util.regex.Pattern;

class Solution {

    // 提前编译验证 IPv4 的正则表达式
    private Pattern IPV4_SEGMENT_PATTERN = Pattern.compile("\\d+");

    // 提前编译验证 IPv6 的正则表达式
    private Pattern IPV6_SEGMENT_PATTERN = Pattern.compile("^[0-9a-f]{1,4}$");

    public String validIPAddress(String queryIP) {
        // 默认答案
        String ans = "Neither";
        // 长度大于 39 则不可能是 IPv4 或者 IPv6
        if (queryIP.length() > 39 || queryIP.length() < 7) {
            return ans;
        }
        // 判断是否是 IPv4
        if (isIPv4(queryIP)) {
            ans = "IPv4";
        }
        // 判断是否是 IPv6
        if (isIPv6(queryIP)) {
            ans = "IPv6";
        }
        return ans;
    }

    private boolean isIPv6(String queryIP) {
        queryIP = queryIP.toLowerCase();
        String[] str = queryIP.split(":", -1);
        // 按照 : 分割后长度为 8
        if (str.length != 8) {
            return false;
        }
        for (String s : str) {
            // 包含有非数字和字母的字符或者字母不在[a~f]范围内
            if (!IPV6_SEGMENT_PATTERN.matcher(s).matches()) {
                return false;
            }
        }
        return true;
    }

    private boolean isIPv4(String queryIP) {
        String[] str = queryIP.split("\\.", -1);
        // 分割后长度为 4
        if (str.length != 4)
            return false;
        for (String s : str) {
            // 不能含有前导0
            if (s.length() > 1 && s.charAt(0) == '0') {
                return false;
            }
            // 包含有非数字字符
            if (!IPV4_SEGMENT_PATTERN.matcher(s).matches()) {
                return false;
            }
            // 不满足区间 0 ~ 255
            int num = Integer.parseInt(s);
            if (num < 0 || num > 255) {
                return false;
            }
        }
        return true;
    }
}

结果分析

  • 时间复杂度: O(N)
  • 空间复杂度: O(N)

笔试 / 面试记录

  1. 中金国际 - 笔试题 ✔ [输入方式: ACM 模式 无输入输出]