一、题目
牛客题目链接:验证IP地址_牛客题霸_牛客网
LeeCode 题目链接:468. 验证IP地址 - 力扣(LeetCode)
题目描述:
给定一个字符串 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 地址。
示例 1:
输入:queryIP = "172.16.254.1"
输出:"IPv4"
解释:有效的 IPv4 地址,返回 "IPv4"
示例 2:
输入:queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334"
输出:"IPv6"
解释:有效的 IPv6 地址,返回 "IPv6"
示例 3:
输入:queryIP = "256.256.256.256"
输出:"Neither"
解释:既不是 IPv4 地址,又不是 IPv6 地址
提示:
queryIP
仅由英文字母,数字,字符'.'
和':'
组成。
二、解题思路&代码实现
方法一:分割字符串比较法(推荐使用)
思路:
先对IP字符串进行分割,然后依次判断每个分割是否符合要求。
具体做法:
- step 1:写一个分割函数split(或者内置函数)。
- step 2:遍历IP字符串,遇到.或者:将其分开储存在一个数组中。
- step 3:遍历数组,对于IPv4,需要依次验证分组为4,分割不能缺省,没有前缀0或者其他字符,数字在0-255范围内。
- step 4:对于IPv6,需要依次验证分组为8,分割不能缺省,每组不能超过4位,不能出现除数字小大写a-f以外的字符。
复杂度分析:
- 时间复杂度:O(n),n为字符串IP的长度,判断部分只遍历4组或者8组,但是分割字符串需要遍历全部
- 空间复杂度:O(1),储存分割字符串的临时空间为常数4或者8。
代码实现:
java:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 验证IP地址
* @param IP string字符串 一个IP地址字符串
* @return string字符串
*/
public String solve (String IP) {
// write code here
if (isIPv4(IP)) {
return "IPv4";
} else if (isIPv6(IP)) {
return "IPv6";
} else {
return "Neither";
}
}
boolean isIPv4(String IP) {
if (IP.indexOf('.') == -1) {
return false;
}
String[] s = IP.split("\\.");
//IPv4 必定为4组
if (s.length != 4) {
return false;
}
for (int i = 0; i < s.length; i++) {
//1.有一个分割为0,说明两个点相连
if (s[i].length() == 0) {
return false;
}
//2.比较数字位数及不为0时不能有前缀零
if (s[i].length() < 0 || s[i].length() > 3 || (s[i].charAt(0) == '0' &&
s[i].length() != 1)) {
return false;
}
int num = 0;
//3.遍历每个分割字符串,必须为数字
for (int j = 0; j < s[i].length(); j++) {
char c = s[i].charAt(j);
if (c < '0' || c > '9') {
return false;
}
}
//4.转为为数字比较,0-255 之间
num = Integer.parseInt(s[i]);
if (num < 0 || num > 255) {
return false;
}
}
return true;
}
boolean isIPv6(String IP) {
if (IP.indexOf(':') == -1) {
return false;
}
String[] s = IP.split(":", -1);
//IPv6 必定为8组
if (s.length != 8) {
return false;
}
for (int i = 0; i < s.length; i++) {
//1.每个分割不能拆过4位
if (s[i].length() == 0 || s[i].length() > 4) {
return false;
}
//2.不能出现a-fA-F以外的大小写字符
for (int j = 0; j < s[i].length(); j++) {
char c = s[i].charAt(j);
boolean expr = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' &&
c <= 'F');
if (!expr) {
return false;
}
}
}
return true;
}
}
php 实现:
<?php
class Solution {
/**
* 验证 IP 地址
* @param string $IP 一个 IP 地址字符串
* @return string "IPv4", "IPv6" 或 "Neither"
*/
public function solve(string $IP): string {
if ($this->isIPv4($IP)) {
return "IPv4";
} elseif ($this->isIPv6($IP)) {
return "IPv6";
} else {
return "Neither";
}
}
private function isIPv4(string $ip): bool {
// 必须包含点号
if (strpos($ip, '.') === false) {
return false;
}
$parts = explode('.', $ip);
// 必须正好是4个部分
if (count($parts) !== 4) {
return false;
}
foreach ($parts as $part) {
// 不能是空字符串
if ($part === '') {
return false;
}
// 长度必须在1~3之间
if (strlen($part) < 1 || strlen($part) > 3) {
return false;
}
// 如果长度大于1,不能以0开头
if (strlen($part) > 1 && $part[0] === '0') {
return false;
}
// 必须全部是数字字符
if (!ctype_digit($part)) {
return false;
}
// 转换为整数并检查范围
$num = intval($part);
if ($num < 0 || $num > 255) {
return false;
}
}
return true;
}
private function isIPv6(string $ip): bool {
// 必须包含冒号
if (strpos($ip, ':') === false) {
return false;
}
$parts = explode(':', $ip);
// 必须正好是8个部分
if (count($parts) !== 8) {
return false;
}
foreach ($parts as $part) {
// 每个部分不能为空或超过4个字符
if ($part === '' || strlen($part) > 4) {
return false;
}
// 检查每个字符是否为合法的十六进制字符
for ($i = 0; $i < strlen($part); $i++) {
$c = $part[$i];
if (!(($c >= '0' && $c <= '9') ||
($c >= 'a' && $c <= 'f') ||
($c >= 'A' && $c <= 'F'))) {
return false;
}
}
}
return true;
}
}
方法二:正则表达式(扩展思路)
思路:
IP地址是有规律可言的:IPv4用了4个0-255的数字,用点隔开,IPv6用了4位十六进制的数字,用冒号隔开,共8组,这都可以直接用正则表达式来表示。
具体做法:
- step 1:IPv4的正则表达式限制数字在0-255,且没有前缀0,用3个点隔开共4组。
- step 2:IPv6的正则表达式限制出现8组,0-9a-fA-F的数,个数必须是1-4个,用冒号隔开。
- step 3:调用函数依次比较给定字符串与模板串之间是否匹配,匹配哪一个就属于哪一种IP地址,否则都不是。
复杂度分析:
- 时间复杂度:O(n),regex_match函数默认O(n)。
- 空间复杂度:O(1),没有使用额外空间。
代码实现:
java:
import java.util.*;
import java.util.regex.Pattern;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 验证IP地址
* @param IP string字符串 一个IP地址字符串
* @return string字符串
*/
public String solve (String IP) {
//正则表达式限制0-255 且没有前缀0 四组齐全
String ipv4 =
"(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
Pattern ipv4_pattern = Pattern.compile(ipv4);
//正则表达式限制出现8组,0-9a-fA-F的数,个数必须是1-4个
String ipv6 = "([0-9a-fA-F]{1,4}\\:){7}[0-9a-fA-F]{1,4}";
Pattern ipv6_pattern = Pattern.compile(ipv6);
//调用正则匹配函数
if (ipv4_pattern.matcher(IP).matches())
return "IPv4";
else if (ipv6_pattern.matcher(IP).matches())
return "IPv6";
else return "Neither";
}
}
<?php
class Solution {
/**
* 验证 IP 地址
* @param string $IP 一个 IP 地址字符串
* @return string "IPv4", "IPv6" 或 "Neither"
*/
public function solve(string $IP): string {
// IPv4 正则:匹配0~255之间的数字,不能有前导0(除非是0本身),共4组
$ipv4Pattern = '/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/';
// IPv6 正则:匹配8组,每组1~4个十六进制字符(0-9a-fA-F)
$ipv6Pattern = '/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/i';
if (preg_match($ipv4Pattern, $IP)) {
return "IPv4";
} elseif (preg_match($ipv6Pattern, $IP)) {
return "IPv6";
} else {
return "Neither";
}
}
}