2020年6月16日 星期二

Leetcode題解 Python & C#:六月挑戰DAY16 Validate IP Address

給一個字串 IP ,回傳其屬於 IPv4 或 IPv6 或都不是 Neighter。 

如果要找「合格」的字串,會想到用 re 去解決,這是在一般的情況下。如果是LeetCode,也許就不會用它。
不過官方的解法是有用到 re 的,直接傻眼,但那確實是最正當的解法。

通過「正則表達式(Regex)」是最嚴謹的做法,但也要有支持這項功能,在未知跟沒有的情況下,只能土炮出來。

這裡要從根本講,不論IPv6或是IPv4對電腦來說都是連續的二進位,IPv4長度為32bit,IPv6為128bit。
給人看的十進位跟十六進位是經由轉換而來的,如果換回原來的二進位形式,就只要留意是否長度是否合格就好。
所以把 string IP 轉換成連續二進位,就能輕而易舉去找出是否為合格的IP位置。(但是寫轉換的過程不容易) 

在 IPv4 中用「.」做分隔,是用十進位的數字,分四段,每一段要有8bit。
在 IPv6 中用「:」做分隔,是用十六進位的數字,分八段,每一段要有32bit。

寫成IP轉換版,轉 IPv6 或 IPv4 的流程是大同小異的,甚至可以只用一個函數修改一些傳入參數就能變成IPv4或IPv6皆可的式子。

Python
class Solution:
    def validIPAddress(self, IP: str) -> str:
        
        deci = {*"0123456789"}
        hexdeci = {*"0123456789abcdef"}
        
        def checkIPv4(IP):
            group = IP.split(".")
            if len(group) != 4: return False
            for seg in group:
                if not(3 >= len(seg) >= 1): return False 
                if any([c not in deci for c in seg]): return False
                if not(255 >= int(seg) >= 0): return False
                if str(int(seg)) != seg: return False
            return True

        def checkIPv6(IP):            
            group = IP.lower().split(":")   
            if len(group) != 8: return False
            for seg in group:
                if not(4 >= len(seg) >= 1): return False                    
                if any([c not in hexdeci for c in seg]): return False                    
            return True
        
        if checkIPv4(IP): return "IPv4"
        if checkIPv6(IP): return "IPv6"
        return "Neither"
Python(IP轉換版)
class Solution:
    def validIPAddress(self, IP: str) -> str:   
        
        decimal = {*"0123456789"}
        
        def covertIPv4(IP):
            IPv4 = ""
            seg = ""
            segCount = 0
            for c in IP:
                if c == ".":
                    segCount += 1
                    if segCount >= 4 or not seg: return False
                    if len(seg) > 1 and seg[0] == '0': return False
                    seg = "{:0>8}".format(bin(int(seg))[2:])
                    if len(seg) > 8: return False
                    IPv4 += seg
                    seg = ""
                elif c in decimal:
                    seg += c
                else:
                    return False
            if seg:                
                if len(seg) > 1 and seg[0] == '0': return False                
                seg = "{:0>8}".format(bin(int(seg))[2:])
                if len(seg) > 8: return False
                IPv4 += seg  
            return len(IPv4) == 32
        
        hexadecimal = {*"0123456789abcdefABCDEF"}
        
        def covertIPv6(IP):
            IPv6 = ""
            seg = ""
            segCount = 0   
            for c in IP:
                if c == ":":   
                    segCount += 1
                    if segCount >= 8 or not seg: return False
                    seg = "{:0>16}".format(seg)
                    if len(seg) > 16: return False
                    IPv6 += seg
                    seg = ""    
                elif c in hexadecimal:
                    seg += "{:0>4}".format(bin(int(c, 16))[2:])
                else:
                    return False
            if seg:                
                seg = "{:0>16}".format(seg)
                if len(seg) > 16: return False
                IPv6 += seg  
            return len(IPv6) == 128                    
            
        if covertIPv4(IP): return "IPv4"
        if covertIPv6(IP): return "IPv6"
        return "Neither"
Python(IP轉換合一版)
class Solution:
    def validIPAddress(self, IP: str) -> str:   
        
        decimal = {*"0123456789"}
        hexadecimal = {*"0123456789abcdef"}
        
        def covertIP(strIP, Segs, PerSegBit, Carry, Delimiter):            
            def addSeg(IP, seg):
                if segCount >= Segs or not seg: return False 
                if Delimiter == "." and len(seg) > 1 and seg[0] == '0': return False   
                if Delimiter == ":" and len(seg) > 4: return False  
                try:
                    seg = ("{:0>"+str(PerSegBit)+"}").format(bin(int(seg, len(Carry)))[2:])
                except:
                    return False
                if len(seg) > PerSegBit: return False
                return IP + seg                
            
            IP = seg = ""
            segCount = 0
            for c in strIP.lower():
                if c == Delimiter:
                    segCount += 1
                    IP = addSeg(IP, seg)
                    if not IP: return False
                    seg = ""
                elif c in Carry:
                    seg += c       
                else:
                    return False    
            if not seg: return False 
            IP = addSeg(IP, seg)
            if not IP: return False           
            return len(IP) == Segs * PerSegBit            
            
        if covertIP(IP, 4, 8, decimal, "."): return "IPv4"
        if covertIP(IP, 8, 16, hexadecimal, ":"): return "IPv6"
        return "Neither"
C#
public class Solution {
    public string ValidIPAddress(string IP) {
        if(IsIPv4(IP)){return "IPv4";}
        if(IsIPv6(IP)){return "IPv6";}
        return "Neither";
    }    
 
    private bool IsIPv4(string IP) {
        var parts = IP.Split('.');
        if(parts.Length != 4){return false;}           
        int dec = 0;
        foreach(var part in parts)
        {
            if(!(Int32.TryParse(part, out dec))){return false;}
            if(dec < 0 || dec > 255 || (dec.ToString().Length != part.Length)){return false;}
        }
        return true;
    }

    private bool IsIPv6(string IP) {
        var parts = IP.Split(':');
        if(parts.Length != 8){return false;}        
        int hex;
        foreach(var part in parts)
        {
            if(part.Length > 4){return false;}
            if(!Int32.TryParse(part, 
                               System.Globalization.NumberStyles.HexNumber, 
                               null,
                               out hex)){return false;}
            if(hex < 0){return false;}
        }
        return true;
    }       
}
TestCase
"1.1.1.01"
"01.01.01.01"
"001.001.001.001"
"301.301.301.301"
"12..33.4"
"1.0.1."
"1.-1.1.1"
"0.0.0.0"
"172.16.254.1"
"256.256.256.256"
"2001:0db8:85a3:0:0:8A2E:0370:7334"
"2001:0db8:85a3::8A2E:0370:7334"
"2001:db8:85a3:0:0:8A2E:0370:7334"
"20EE:FGb8:85a3:0:0:8A2E:0370:7334"
"2001:0db8:85a3:00000:0:8A2E:0370:7334"
"1081:db8:85a3:01:-0:8A2E:0370:7334"
"2001:0db8:85a3:0:0:8A2E:0370:7334:"